home *** CD-ROM | disk | FTP | other *** search
/ Windows Game Programming for Dummies (2nd Edition) / WinGamProgFD.iso / pc / DirectX SDK / DXSDK / extras / Direct3D / Tools / 3DSMax4 / ExportXFile.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2001-10-08  |  120.3 KB  |  4,025 lines

  1. //-----------------------------------------------------------------------------
  2. // File: ExportXFile.cpp
  3. //
  4. // Desc: Functions used to export max data to an X File
  5. //
  6. // Copyright (C) 1998-2000 Microsoft Corporation. All Rights Reserved.
  7. //-----------------------------------------------------------------------------
  8.  
  9. #include "pch.h"
  10. #include "XSkinExp.h"
  11. #include "MeshData.h"
  12.  
  13. #include <initguid.h>
  14.  
  15. #include "XSkinExpTemplates.h"
  16. #include "dxfile.h"
  17. #include "rmxfguid.h"
  18. #include "rmxftmpl.h"
  19.  
  20. #define POWER_DEFAULT   0.0
  21.  
  22. struct SDialogOptions
  23. {
  24.     BOOL                        m_bProceedWithExport;
  25.     DXFILEFORMAT                m_xFormat;
  26.     DWORD                       m_cMaxBonesPerVertex;
  27.     DWORD                       m_cMaxBonesPerFace;
  28.  
  29.     BOOL                        m_bSavePatchData;
  30.     BOOL                        m_bSaveAnimationData;
  31.     BOOL                        m_bLoopingAnimationData;
  32.     DWORD                       m_iAnimSamplingRate;
  33. };
  34.  
  35. struct SBoneInfo
  36. {
  37.     INode                       *m_pBoneNode;
  38.     DWORD                       m_cVertices;
  39. };
  40.  
  41. // structure used to map an mesh to a bone info structure
  42. struct SSkinMap
  43. {
  44.     SSkinMap()
  45.         :m_pMeshNode(NULL), m_rgbiBones(NULL), m_cbiBones(0), m_cbiBonesMax(0) {}
  46.     ~SSkinMap()
  47.         { delete []m_rgbiBones; }
  48.  
  49.     INode                       *m_pMeshNode;
  50.  
  51.     SBoneInfo                   *m_rgbiBones;
  52.     DWORD                       m_cbiBones;
  53.     DWORD                       m_cbiBonesMax;
  54.  
  55.     SBoneInfo *FindBone(INode *pBoneNode)
  56.     {
  57.         SBoneInfo *pbi = NULL;
  58.         DWORD iBone;
  59.  
  60.         for (iBone = 0; iBone < m_cbiBones; iBone++)
  61.         {
  62.             if (pBoneNode == m_rgbiBones[iBone].m_pBoneNode)
  63.             {
  64.                 pbi = &m_rgbiBones[iBone];
  65.                 break;
  66.             }
  67.         }
  68.  
  69.         return pbi;
  70.     }
  71.  
  72.     HRESULT AddBone(INode *pBoneNode, SBoneInfo **ppbiBoneInfo)
  73.     {
  74.         HRESULT hr = S_OK;
  75.         SBoneInfo *rgbiTemp;
  76.  
  77.         // reallocate if neccessary
  78.         if (m_cbiBones == m_cbiBonesMax)
  79.         {
  80.             m_cbiBonesMax = max(1, m_cbiBonesMax);
  81.             m_cbiBonesMax *= 2;
  82.  
  83.             rgbiTemp = m_rgbiBones;
  84.             m_rgbiBones = new SBoneInfo[m_cbiBonesMax];
  85.             if (m_rgbiBones == NULL)
  86.             {
  87.                 m_rgbiBones = rgbiTemp;
  88.                 hr = E_OUTOFMEMORY;
  89.                 goto e_Exit;
  90.             }
  91.  
  92.             if (m_cbiBones > 0)
  93.             {
  94.                 memcpy(m_rgbiBones, rgbiTemp, m_cbiBones * sizeof(SBoneInfo));
  95.             }
  96.  
  97.             delete []rgbiTemp;
  98.         }
  99.  
  100.         // not initialize the next bone in the array and return a pointer to it
  101.  
  102.         m_rgbiBones[m_cbiBones].m_cVertices = 0;
  103.         m_rgbiBones[m_cbiBones].m_pBoneNode = pBoneNode;
  104.  
  105.         *ppbiBoneInfo = &m_rgbiBones[m_cbiBones];
  106.  
  107.         m_cbiBones += 1;
  108.  
  109. e_Exit:
  110.         return hr;
  111.     }
  112. };
  113.  
  114. struct SPreprocessContext
  115. {
  116.     SPreprocessContext()
  117.         :m_rgpsmSkinMaps(NULL), 
  118.          m_cpsmSkinMaps(NULL), 
  119.          m_cpsmSkinMapsMax(0), 
  120.          m_cMaxWeightsPerVertex(0), 
  121.          m_cMaxWeightsPerFace(0),
  122.          m_cNodes(0) {}
  123.  
  124.     ~SPreprocessContext()
  125.         { delete []m_rgpsmSkinMaps; }
  126.  
  127.     BOOL                        m_bSaveSelection;
  128.  
  129.     SSkinMap                    **m_rgpsmSkinMaps;
  130.     DWORD                       m_cpsmSkinMaps;
  131.     DWORD                       m_cpsmSkinMapsMax;
  132.  
  133.     DWORD                       m_cMaxWeightsPerVertex;
  134.     DWORD                       m_cMaxWeightsPerFace;
  135.  
  136.     DWORD                       m_cNodes;
  137. };
  138.  
  139. const int x_cbStringBufferMax = 4088;
  140.  
  141. struct SStringBlock
  142. {
  143.     SStringBlock()
  144.         :m_psbNext(NULL), m_cbData(0) {}
  145.     ~SStringBlock()
  146.     {
  147.         delete m_psbNext;
  148.     }
  149.  
  150.     SStringBlock                *m_psbNext;
  151.     DWORD                        m_cbData;
  152.  
  153.     TCHAR                        szData[x_cbStringBufferMax];
  154. };
  155.  
  156. class CStringTable
  157. {
  158. public:
  159.     CStringTable()
  160.         :m_psbHead(NULL) {}
  161.  
  162.     ~CStringTable()
  163.     {
  164.         delete m_psbHead;
  165.     }
  166.  
  167.     // allocate a string out of the data blocks to be free'd later, and make it a valid
  168.     //   x-file name at the same time
  169.     TCHAR *CreateNiceString(TCHAR *szString)
  170.     {
  171.         TCHAR* szNewString = NULL;
  172.         BOOL bFirstCharIsDigit;
  173.         DWORD cbLength;
  174.         SStringBlock *psbNew;
  175.  
  176.         if (szString == NULL)
  177.             return NULL;
  178.  
  179.         cbLength = _tcslen(szString) + 1;
  180.  
  181.         bFirstCharIsDigit = _istdigit(*szString);
  182.         if (bFirstCharIsDigit)
  183.         {
  184.             cbLength += 1;
  185.         }
  186.  
  187.         // if no string blocks or the current doesn't have enough space, then allocate one
  188.         if ((m_psbHead == NULL) || ((x_cbStringBufferMax - m_psbHead->m_cbData) < cbLength))
  189.         {
  190.             psbNew = new SStringBlock();
  191.             if (psbNew == NULL)
  192.                 return NULL;
  193.  
  194.             psbNew->m_psbNext = m_psbHead;
  195.             m_psbHead = psbNew;
  196.         }
  197.  
  198.         // allocate a string out of the data block
  199.         szNewString = m_psbHead->szData + m_psbHead->m_cbData;
  200.         m_psbHead->m_cbData += cbLength;
  201.  
  202.         // deal with the fact that the string can't start with digits
  203.         *szNewString = _T('\0');
  204.         if( bFirstCharIsDigit ) 
  205.         {
  206.             _tcscat(szNewString, _T("_"));
  207.         }
  208.  
  209.         _tcscat(szNewString, szString);
  210.  
  211.         TCHAR* pchCur = szNewString;
  212.         while( NULL != *pchCur )
  213.         {
  214.             if( *pchCur != _T('_') && !_istalnum(*pchCur) )
  215.             {
  216.                 *pchCur = _T('_');
  217.             }
  218.             pchCur++;
  219.         }
  220.         return szNewString;
  221.     }
  222.  
  223.     // Allocate a new string with '\\' in place of '\' characters
  224.     TCHAR* CreateNiceFilename(TCHAR *szString)
  225.     {
  226.         TCHAR* szNewString = NULL;
  227.         DWORD cbNameLength;
  228.         DWORD cbLength;
  229.         TCHAR* pchCur;
  230.         TCHAR* pchOrig;
  231.         SStringBlock *psbNew;
  232.  
  233.         if( NULL == szString )
  234.         {
  235.             return NULL;
  236.         }
  237.  
  238.         cbNameLength = _tcslen(szString);
  239.         cbLength = cbNameLength*2 + 1;
  240.  
  241.  
  242.         // if no string blocks or the current doesn't have enough space, then allocate one
  243.         if ((m_psbHead == NULL) || ((x_cbStringBufferMax - m_psbHead->m_cbData) < cbLength))
  244.         {
  245.             psbNew = new SStringBlock();
  246.             if (psbNew == NULL)
  247.                 return NULL;
  248.  
  249.             psbNew->m_psbNext = m_psbHead;
  250.             m_psbHead = psbNew;
  251.         }
  252.  
  253.         // allocate a string out of the data block
  254.         szNewString = m_psbHead->szData + m_psbHead->m_cbData;
  255.         m_psbHead->m_cbData += cbLength;
  256.  
  257.         pchCur = szNewString;
  258.         pchOrig = szString;
  259.         while (NULL != *pchOrig)
  260.         {
  261.             if( _T('\\') == *pchOrig )
  262.             {
  263.                 *(pchCur++) = _T('\\');
  264.                 *(pchCur++) = _T('\\');
  265.             }
  266.             else
  267.             {
  268.                 *(pchCur++) = *pchOrig;
  269.             }
  270.             pchOrig++;
  271.         }
  272.         *pchCur = _T('\0');
  273.  
  274.         return szNewString;
  275.     }
  276.  
  277.     // Allocate a new string without fiddling with the '\' characters
  278.     TCHAR* CreateNormalFilename(TCHAR *szString)
  279.     {
  280.         TCHAR* szNewString = NULL;
  281.         DWORD cbNameLength;
  282.         DWORD cbLength;
  283.         SStringBlock *psbNew;
  284.  
  285.         if( NULL == szString )
  286.         {
  287.             return NULL;
  288.         }
  289.  
  290.         cbNameLength = _tcslen(szString);
  291.         cbLength = cbNameLength + 1;
  292.  
  293.  
  294.         // if no string blocks or the current doesn't have enough space, then allocate one
  295.         if ((m_psbHead == NULL) || ((x_cbStringBufferMax - m_psbHead->m_cbData) < cbLength))
  296.         {
  297.             psbNew = new SStringBlock();
  298.             if (psbNew == NULL)
  299.                 return NULL;
  300.  
  301.             psbNew->m_psbNext = m_psbHead;
  302.             m_psbHead = psbNew;
  303.         }
  304.  
  305.         // allocate a string out of the data block
  306.         szNewString = m_psbHead->szData + m_psbHead->m_cbData;
  307.         m_psbHead->m_cbData += cbLength;
  308.  
  309.         memcpy(szNewString, szString, cbLength);
  310.  
  311.         return szNewString;
  312.     }
  313. private:
  314.     SStringBlock *m_psbHead;
  315. };
  316.  
  317. struct SSaveContext
  318. {
  319.     SSaveContext()
  320.         :m_rgpsmSkinMaps(NULL), m_pAnimationSet(NULL) {}
  321.  
  322.     ~SSaveContext()
  323.         { delete []m_rgpsmSkinMaps; }
  324.  
  325.     LPDIRECTXFILESAVEOBJECT     m_pxofsave;
  326.     DXFILEFORMAT                m_xFormat;
  327.     DWORD                       m_iTime;
  328.     BOOL                        m_bSaveSelection;
  329.     BOOL                        m_bSavePatchData;
  330.     BOOL                        m_bSaveAnimationData;
  331.     BOOL                        m_bLoopingAnimationData;
  332.     DWORD                       m_iAnimSamplingRate;
  333.     DWORD                       m_cMaxWeightsPerVertex;
  334.     DWORD                       m_cMaxWeightsPerFace;
  335.  
  336.     SSkinMap                    **m_rgpsmSkinMaps;
  337.     DWORD                       m_cpsmSkinMaps;
  338.  
  339.     LPDIRECTXFILEDATA           m_pAnimationSet;
  340.     Interface                   *m_pInterface;
  341.  
  342.     DWORD                       m_cNodes;
  343.     DWORD                       m_cNodesCur;
  344.     INode                       **m_rgpnNodes;
  345.  
  346.     CStringTable                m_stStrings;
  347.  
  348.     SSkinMap *GetSkinMap(INode *pMeshNode)
  349.     {
  350.         SSkinMap *psm = NULL;
  351.         DWORD iMesh;
  352.  
  353.         for (iMesh = 0; iMesh < m_cpsmSkinMaps; iMesh++)
  354.         {
  355.             if (pMeshNode == m_rgpsmSkinMaps[iMesh]->m_pMeshNode)
  356.             {
  357.                 psm = m_rgpsmSkinMaps[iMesh];
  358.                 break;
  359.             }
  360.         }
  361.  
  362.         return psm;
  363.     }
  364.  
  365. };
  366.  
  367.  
  368. // Macros for saving data to memory at DWORD* pbCur (this pointer is incremented)
  369. #define WRITE_PTCHAR(pbCur, ptchar) {TCHAR** __pptchar = (TCHAR**)pbCur; *(__pptchar++) = ptchar;\
  370.                              pbCur = (PBYTE)__pptchar;}
  371.  
  372. #define WRITE_STRING(pbCur, pstring) {TCHAR* __pCurrDestChar = (TCHAR*)pbCur; TCHAR* __pCurrOrgChar = pstring;\
  373.                                 while(NULL != *__pCurrOrgChar) { *(__pCurrDestChar++) = *(__pCurrOrgChar++); }\
  374.                                 *(__pCurrDestChar++) = _T('\0'); pbCur = __pCurrDestChar;}\
  375.  
  376. #define WRITE_WORD(pbCur, word) {WORD* __pword = (WORD*)pbCur; *(__pword++) = word;\
  377.                              pbCur = (PBYTE)__pword;}
  378.  
  379. #define WRITE_DWORD(pbCur, dword) {DWORD* __pdword = (DWORD*)pbCur; *(__pdword++) = dword;\
  380.                              pbCur = (PBYTE)__pdword;}
  381.  
  382. #define WRITE_FLOAT(pbCur, _float) {float* __pfloat = (float*)pbCur; *(__pfloat++) = _float;\
  383.                              pbCur = (PBYTE)__pfloat;}
  384.  
  385. #define WRITE_POINT3(pbCur, _point3) {Point3 _temp = (Point3)_point3; float __tempVal;\
  386.                                __tempVal = _temp[0]; WRITE_FLOAT(pbCur, __tempVal);\
  387.                                __tempVal = _temp[1]; WRITE_FLOAT(pbCur, __tempVal);\
  388.                                __tempVal = _temp[2]; WRITE_FLOAT(pbCur, __tempVal);}
  389.  
  390. #define WRITE_COLOR(pbCur, _color) {D3DXCOLOR _temp = (D3DXCOLOR)_color; float __tempVal;\
  391.                                __tempVal = _temp.r; WRITE_FLOAT(pbCur, __tempVal);\
  392.                                __tempVal = _temp.g; WRITE_FLOAT(pbCur, __tempVal);\
  393.                                __tempVal = _temp.b; WRITE_FLOAT(pbCur, __tempVal);\
  394.                                __tempVal = _temp.a; WRITE_FLOAT(pbCur, __tempVal);}
  395.  
  396. #define WRITE_MATRIX4_FROM_MATRIX3(pbCur, _matrix3) {Point3 __tempRow = ((Matrix3)_matrix3).GetRow(0);\
  397.                                 WRITE_POINT3(pbCur, __tempRow); WRITE_FLOAT(pbCur, 0);\
  398.                                 __tempRow = _matrix3.GetRow(1); WRITE_POINT3(pbCur, __tempRow); WRITE_FLOAT(pbCur, 0);\
  399.                                 __tempRow = _matrix3.GetRow(2); WRITE_POINT3(pbCur, __tempRow); WRITE_FLOAT(pbCur, 0);\
  400.                                 __tempRow = _matrix3.GetRow(3); WRITE_POINT3(pbCur, __tempRow); WRITE_FLOAT(pbCur, 1);}
  401.  
  402. #define WRITE_MATRIX(pbCur, mat) { *(D3DXMATRIX*)pbCur = mat;\
  403.                                pbCur = (PBYTE)pbCur + sizeof(D3DXMATRIX);}
  404.  
  405. const GUID* aIds[] = {&DXFILEOBJ_XSkinMeshHeader,
  406.                 &DXFILEOBJ_VertexDuplicationIndices,
  407.                 &DXFILEOBJ_SkinWeights};
  408.  
  409.  
  410.  
  411.  
  412. BOOL CALLBACK XSkinExpOptionsDlgProc
  413.     (
  414.     HWND hWnd,
  415.     UINT message,
  416.     WPARAM wParam,
  417.     LPARAM lParam
  418.     ) 
  419. {
  420.     static SDialogOptions *pDialogOptions = NULL;
  421.     TCHAR buff[8];
  422.  
  423.     switch(message) 
  424.     {
  425.         case WM_INITDIALOG:
  426.             pDialogOptions = (SDialogOptions *)lParam;
  427.  
  428.             CenterWindow(hWnd,GetParent(hWnd));
  429.  
  430.             CheckDlgButton(hWnd, IDC_ANIMATION, 
  431.                     pDialogOptions->m_bSaveAnimationData ? BST_CHECKED : BST_UNCHECKED);
  432.  
  433.             CheckDlgButton(hWnd, IDC_LOOPINGANIMATION, 
  434.                     pDialogOptions->m_bLoopingAnimationData ? BST_CHECKED : BST_UNCHECKED);
  435.  
  436.             CheckDlgButton(hWnd, IDC_PATCHDATA, 
  437.                     pDialogOptions->m_bSavePatchData ? BST_CHECKED : BST_UNCHECKED);
  438.  
  439.             if (pDialogOptions == NULL)
  440.                 return FALSE;
  441.  
  442.             switch (pDialogOptions->m_xFormat)
  443.             {
  444.                 case DXFILEFORMAT_BINARY:
  445.                     CheckRadioButton(hWnd,IDC_TEXT,IDC_BINARYCOMPRESSED,IDC_BINARY);
  446.                     break;
  447.  
  448.                 case DXFILEFORMAT_TEXT:
  449.                     CheckRadioButton(hWnd,IDC_TEXT,IDC_BINARYCOMPRESSED,IDC_TEXT);
  450.                     break;
  451.  
  452.                 case DXFILEFORMAT_BINARY | DXFILEFORMAT_COMPRESSED:
  453.                     CheckRadioButton(hWnd,IDC_TEXT,IDC_BINARYCOMPRESSED,IDC_BINARYCOMPRESSED);
  454.                     break;
  455.             }
  456.  
  457.             _stprintf(buff,_T("%d"),pDialogOptions->m_cMaxBonesPerVertex);
  458.             SetDlgItemText(hWnd,IDC_MAX_BONES_PER_VERTEX,buff);
  459.  
  460.             _stprintf(buff,_T("%d"),pDialogOptions->m_cMaxBonesPerFace);
  461.             SetDlgItemText(hWnd,IDC_MAX_BONES_PER_FACE,buff);
  462.  
  463.             _stprintf(buff,_T("%d"),pDialogOptions->m_iAnimSamplingRate);
  464.             SetDlgItemText(hWnd,IDC_SAMPLERATE,buff);
  465.  
  466.             return TRUE;
  467.  
  468.         case WM_CLOSE:
  469.             pDialogOptions->m_bProceedWithExport = FALSE;
  470.  
  471.             EndDialog(hWnd, 0);
  472.             return TRUE;
  473.  
  474.         case WM_COMMAND:
  475.             switch(LOWORD(wParam))
  476.             {
  477.             case IDC_GO:
  478.                 pDialogOptions->m_bProceedWithExport = TRUE;
  479.  
  480.                 EndDialog(hWnd,0);
  481.                 break;
  482.  
  483.             case IDC_CANCEL:
  484.                 pDialogOptions->m_bProceedWithExport = FALSE;
  485.  
  486.                 EndDialog(hWnd,0);
  487.                 break;
  488.  
  489.             case IDC_TEXT:
  490.                 pDialogOptions->m_xFormat = DXFILEFORMAT_TEXT;
  491.                 break;
  492.  
  493.             case IDC_BINARY:
  494.                 pDialogOptions->m_xFormat = DXFILEFORMAT_BINARY;
  495.                 break;
  496.  
  497.             case IDC_BINARYCOMPRESSED:
  498.                 pDialogOptions->m_xFormat = DXFILEFORMAT_BINARY | DXFILEFORMAT_COMPRESSED;
  499.                 break;
  500.  
  501.             case IDC_ANIMATION:
  502.                 pDialogOptions->m_bSaveAnimationData = !pDialogOptions->m_bSaveAnimationData;
  503.                 break;
  504.  
  505.             case IDC_LOOPINGANIMATION:
  506.                 pDialogOptions->m_bLoopingAnimationData = !pDialogOptions->m_bLoopingAnimationData;
  507.                 break;
  508.  
  509.             case IDC_PATCHDATA:
  510.                 pDialogOptions->m_bSavePatchData = !pDialogOptions->m_bSavePatchData;
  511.                 break;
  512.  
  513.             case IDC_SAMPLERATE:
  514.                 GetDlgItemText(hWnd,IDC_SAMPLERATE,buff,sizeof(buff) / sizeof(TCHAR));
  515.  
  516.                 pDialogOptions->m_iAnimSamplingRate = _ttoi(buff);
  517.                 break;
  518.  
  519.             default:
  520.                 break;
  521.             }
  522.     }
  523.     return FALSE;
  524. }
  525.  
  526. // ================================================== FindPhysiqueModifier()
  527. // Find if a given node contains a Physique Modifier
  528. // DerivedObjectPtr requires you include "modstack.h" from the MAX SDK
  529. Modifier* FindPhysiqueModifier (INode* nodePtr)
  530. {
  531.     // Get object from node. Abort if no object.
  532.     Object* ObjectPtr = nodePtr->GetObjectRef();
  533.     
  534.  
  535.     if ( NULL == ObjectPtr)
  536.     {
  537.         return NULL;
  538.     }
  539.  
  540.     // Is derived object ?
  541.     if (ObjectPtr->SuperClassID() == GEN_DERIVOB_CLASS_ID)
  542.     {
  543.         // Yes -> Cast.
  544.         IDerivedObject* DerivedObjectPtr = static_cast<IDerivedObject*>(ObjectPtr);
  545.  
  546.         // Iterate over all entries of the modifier stack.
  547.         int ModStackIndex = 0;
  548.         while (ModStackIndex < DerivedObjectPtr->NumModifiers())
  549.         {
  550.             // Get current modifier.
  551.             Modifier* ModifierPtr = DerivedObjectPtr->GetModifier(ModStackIndex);
  552.             Class_ID clsid = ModifierPtr->ClassID();
  553.             // Is this Physique ?
  554.             if (ModifierPtr->ClassID() == Class_ID(PHYSIQUE_CLASS_ID_A, PHYSIQUE_CLASS_ID_B))
  555.             {
  556.                 // Yes -> Exit.
  557.                 return ModifierPtr;
  558.             }
  559.  
  560.             // Next modifier stack entry.
  561.             ModStackIndex++;
  562.         }
  563.     }
  564.  
  565.     // Not found.
  566.     return NULL;
  567. }
  568.  
  569. // ================================================== GetTriObjectFromObjRef
  570. // Return a pointer to a TriObject given an object reference or return NULL
  571. // if the node cannot be converted to a TriObject
  572. TriObject *GetTriObjectFromObjRef
  573.     (
  574.     Object* pObj, 
  575.     BOOL *pbDeleteIt
  576.     ) 
  577. {
  578.     TriObject *pTri;
  579.  
  580.     assert(pObj != NULL);
  581.     assert(pbDeleteIt != NULL);
  582.  
  583.     *pbDeleteIt = FALSE;
  584.  
  585.     if (pObj->CanConvertToType(Class_ID(TRIOBJ_CLASS_ID, 0))) 
  586.     { 
  587.         pTri = (TriObject *) pObj->ConvertToType(0, Class_ID(TRIOBJ_CLASS_ID, 0));
  588.  
  589.         // Note that the TriObject should only be deleted
  590.         // if the pointer to it is not equal to the object
  591.         // pointer that called ConvertToType()
  592.         if (pObj != pTri) 
  593.             *pbDeleteIt = TRUE;
  594.  
  595.         return pTri;
  596.     } 
  597.     else 
  598.     {
  599.         return NULL;
  600.     }
  601. }
  602.  
  603. // ================================================== GetTriObjectFromObjRef
  604. // Return a pointer to a TriObject given an object reference or return NULL
  605. // if the node cannot be converted to a TriObject
  606. PatchObject *GetPatchObjectFromObjRef
  607.     (
  608.     Object* pObj, 
  609.     BOOL *pbDeleteIt
  610.     ) 
  611. {
  612.     PatchObject *pPatchObject;
  613.  
  614.     assert(pObj != NULL);
  615.     assert(pbDeleteIt != NULL);
  616.  
  617.     *pbDeleteIt = FALSE;
  618.  
  619.     if (pObj->CanConvertToType(Class_ID(PATCHOBJ_CLASS_ID, 0))) 
  620.     { 
  621.         pPatchObject = (PatchObject *) pObj->ConvertToType(0, Class_ID(PATCHOBJ_CLASS_ID, 0));
  622.  
  623.         // Note that the PatchObject should only be deleted
  624.         // if the pointer to it is not equal to the object
  625.         // pointer that called ConvertToType()
  626.         if (pObj != pPatchObject) 
  627.             *pbDeleteIt = TRUE;
  628.  
  629.         return pPatchObject;
  630.     } 
  631.     else 
  632.     {
  633.         return NULL;
  634.     }
  635. }
  636.  
  637. // ================================================== IsExportableMesh()
  638. BOOL IsExportableMesh(INode* pNode, Object* &pObj)
  639. {
  640.     ULONG superClassID;
  641.  
  642.     assert(pNode);
  643.     pObj = pNode->GetObjectRef();
  644.  
  645.     if (pObj == NULL)
  646.     {
  647.         return FALSE;
  648.     }
  649.  
  650.     superClassID = pObj->SuperClassID();
  651.     //find out if mesh is renderable
  652.  
  653.     if( !pNode->Renderable() || pNode->IsNodeHidden())
  654.     {
  655.         return FALSE;
  656.     }
  657.  
  658.     BOOL bFoundGeomObject = FALSE;
  659.     //find out if mesh is renderable (more)
  660.     switch(superClassID)
  661.     {
  662.     case GEN_DERIVOB_CLASS_ID:
  663.  
  664.         do
  665.         {
  666.             pObj = ((IDerivedObject*)pObj)->GetObjRef();
  667.             superClassID = pObj->SuperClassID();
  668.         }
  669.         while( superClassID == GEN_DERIVOB_CLASS_ID );
  670.  
  671.         switch(superClassID)
  672.         {
  673.         case GEOMOBJECT_CLASS_ID:
  674.             bFoundGeomObject = TRUE;
  675.             break;
  676.         }
  677.         break;
  678.  
  679.     case GEOMOBJECT_CLASS_ID:
  680.         bFoundGeomObject = TRUE;
  681.  
  682.     default:
  683.         break;
  684.     }
  685.  
  686.     return bFoundGeomObject;
  687. }
  688.  
  689. // ================================================== IsExportablePatchMesh()
  690. BOOL IsExportablePatchMesh(INode* pNode, Object* &pObj)
  691. {
  692.     ULONG superClassID;
  693.     Object *pObjBase;
  694.  
  695.     assert(pNode != NULL);
  696.  
  697.     pObj = pNode->GetObjectRef();
  698.     if (pObj == NULL)
  699.         return FALSE;
  700.  
  701.     if( !pNode->Renderable() || pNode->IsNodeHidden())
  702.         return FALSE;
  703.  
  704.  
  705.     pObjBase = pObj;
  706.  
  707.     superClassID = pObjBase->SuperClassID();
  708.     switch(superClassID)
  709.     {
  710.     case GEN_DERIVOB_CLASS_ID:
  711.  
  712.         do
  713.         {
  714.             pObjBase = ((IDerivedObject*)pObjBase)->GetObjRef();
  715.             superClassID = pObjBase->SuperClassID();
  716.         }
  717.         while( superClassID == GEN_DERIVOB_CLASS_ID );
  718.  
  719.         switch(superClassID)
  720.         {
  721.         case GEOMOBJECT_CLASS_ID:
  722.             break;
  723.         }
  724.         break;
  725.  
  726.     case GEOMOBJECT_CLASS_ID:
  727.         break;
  728.  
  729.     default:
  730.         break;
  731.     }
  732.  
  733.     pObj = pObjBase;
  734.  
  735.     // if the base object is a patch based object
  736.     if ((pObjBase->ClassID() == Class_ID(PATCHOBJ_CLASS_ID, 0))
  737.         || (pObjBase->ClassID() == Class_ID(PATCHGRID_CLASS_ID, 0)))
  738.     {
  739.         // then check to make sure that the intervening steps don't disallow
  740.         //   a patch object to be converted to
  741.         return pObjBase->CanConvertToType(Class_ID(PATCHOBJ_CLASS_ID, 0));
  742.     }
  743.     else
  744.         return FALSE;
  745. }
  746.  
  747. // ================================================== dummyFn()
  748. // used by 3DS progress bar
  749. DWORD WINAPI dummyFn(LPVOID arg)
  750. {
  751.     return(0);
  752. }
  753.  
  754. // -------------------------------------------------------------------------------
  755. //   class      CFindRootNode
  756. //
  757. //   devnote    Helper class for FindRootNode, used to implement callback function
  758. //                  for scene traversal.  Finds the root node by aborting the search
  759. //                  after the first node is returned.  It takes the first node and
  760. //                  walks to the root from there.
  761. //
  762. class CFindRootNode : public ITreeEnumProc
  763. {
  764. public:
  765.     CFindRootNode()
  766.         :m_pNodeRoot(NULL) {}
  767.  
  768.     virtual int callback(INode *pNode)
  769.     {
  770.         INode *pNodeCur = pNode;
  771.  
  772.         while (!pNodeCur->IsRootNode())
  773.         {
  774.             pNodeCur = pNodeCur->GetParentNode();
  775.         }
  776.         m_pNodeRoot = pNodeCur;
  777.  
  778.         return TREE_ABORT;
  779.     }
  780.  
  781.     INode *m_pNodeRoot;
  782. };
  783.  
  784. // -------------------------------------------------------------------------------
  785. //  function    FindRootNode
  786. //
  787. //   devnote    Finds the root node of the scene by aborting a tree walk after the first node
  788. //
  789. //   returns    S_OK if a node is found, E_FAIL if not
  790. //
  791. HRESULT FindRootNode
  792.     (
  793.     IScene *pScene, 
  794.     INode **ppNode
  795.     )
  796. {
  797.     CFindRootNode RootNode;
  798.  
  799.     // grab the first node of the scene graph
  800.     pScene->EnumTree(&RootNode);
  801.  
  802.     *ppNode = RootNode.m_pNodeRoot;
  803.  
  804.     return RootNode.m_pNodeRoot != NULL ? S_OK : E_FAIL;
  805. }
  806.  
  807. HRESULT AddNormals
  808.     (
  809.     SSaveContext *psc,
  810.     SMeshData *pMeshData, 
  811.     BOOL bSwapTriOrder,
  812.     LPDIRECTXFILEDATA pParent
  813.     )
  814. {
  815.     HRESULT hr = S_OK;
  816.     LPDIRECTXFILEDATA pDataObject = NULL;
  817.     PBYTE         pbData = NULL;
  818.     PBYTE         pbCur;        
  819.     DWORD          cbSize;
  820.     DWORD cNormals;
  821.     DWORD cFaces;
  822.     DWORD iFace;
  823.     DWORD iVertex;
  824.  
  825.     assert(psc != NULL);
  826.     assert(pParent != NULL);
  827.     assert(pMeshData != NULL);
  828.  
  829.     cNormals = pMeshData->m_cVertices;
  830.     cFaces = pMeshData->m_cFaces;
  831.  
  832.     cbSize = sizeof(DWORD) // nNormals
  833.              + 3*sizeof(float)*cNormals // normals
  834.              + sizeof(DWORD) // nFaces
  835.              + cFaces* // MeshFace array
  836.                 (sizeof(DWORD) //nFaceVertexIndices (number of normal indices)
  837.                  + 3*sizeof(DWORD)); // faceVertexIndices (normal indices)
  838.  
  839.     pbCur = pbData = new BYTE[cbSize];
  840.     if (pbData == NULL)
  841.     {
  842.         hr = E_OUTOFMEMORY;
  843.         goto e_Exit;
  844.     }
  845.  
  846.     // nNormals
  847.     WRITE_DWORD(pbCur, cNormals);
  848.  
  849.     // normals
  850.     for (iVertex = 0; iVertex < pMeshData->m_cVertices; iVertex++)
  851.     {
  852.         WRITE_POINT3(pbCur, pMeshData->m_rgVertices[iVertex].vNormal);
  853.     }
  854.  
  855.     // nFaces
  856.     WRITE_DWORD(pbCur, cFaces);
  857.  
  858.  
  859.     // MeshFace array
  860.     for( iFace = 0; iFace < cFaces; iFace++ )
  861.     {
  862.         WRITE_DWORD(pbCur, 3); // nFaceVertexIndices (number of normal indices)
  863.  
  864.         // faceVertexIndices (indices to normals - same as indices to vertices for us)
  865.         if( bSwapTriOrder )
  866.         {
  867.             WRITE_DWORD(pbCur, pMeshData->m_rgFaces[iFace].index[0]);
  868.             WRITE_DWORD(pbCur, pMeshData->m_rgFaces[iFace].index[2]);
  869.             WRITE_DWORD(pbCur, pMeshData->m_rgFaces[iFace].index[1]);
  870.         }
  871.         else
  872.         {
  873.             WRITE_DWORD(pbCur, pMeshData->m_rgFaces[iFace].index[0]);
  874.             WRITE_DWORD(pbCur, pMeshData->m_rgFaces[iFace].index[1]);
  875.             WRITE_DWORD(pbCur, pMeshData->m_rgFaces[iFace].index[2]);
  876.         }
  877.     }
  878.  
  879.     hr = psc->m_pxofsave->CreateDataObject(TID_D3DRMMeshNormals,
  880.                                     NULL,
  881.                                     NULL,
  882.                                     cbSize,
  883.                                     pbData,
  884.                                     &pDataObject
  885.                                     );
  886.     if (FAILED(hr))
  887.     {
  888.         OutputDebugString("Failed to create x file data object!");
  889.         goto e_Exit;
  890.     }
  891.  
  892.     hr = pParent->AddDataObject(pDataObject);
  893.     if (FAILED(hr))
  894.     {
  895.         OutputDebugString("Failed to add x file data object!");
  896.         goto e_Exit;
  897.     }
  898.  
  899.     // falling through
  900. e_Exit:
  901.     RELEASE(pDataObject);
  902.     delete []pbData;
  903.  
  904.     return hr;
  905. }
  906.  
  907. HRESULT AddTextureCoordinates
  908.     (
  909.     SSaveContext *psc,
  910.     SMeshData *pMeshData, 
  911.     SCropInfo *rgCropInfo,
  912.     Mesh* pMesh,
  913.     LPDIRECTXFILEDATA pParent
  914.     )
  915. {
  916.     LPDIRECTXFILEDATA pDataObject = NULL;
  917.     PBYTE         pbData = NULL;
  918.     PBYTE         pbCur = NULL;
  919.     DWORD         cbSize;
  920.     DWORD         cTexCoords;
  921.     DWORD         iVertex;
  922.     DWORD         iTextureIndex;
  923.     HRESULT    hr = S_OK;
  924.     float         fX;
  925.     float         fY;
  926.     DWORD         iMaterial;
  927.  
  928.     assert( psc );
  929.     assert( pParent );
  930.     assert( pMesh );
  931.     assert( pMeshData );
  932.  
  933.     // if no tex coords, then don't add them
  934.     if( !pMeshData->m_bTexCoordsPresent )
  935.         goto e_Exit;
  936.  
  937.     cTexCoords = pMeshData->m_cVertices;
  938.  
  939.     cbSize = sizeof(DWORD) //nTextureCoords
  940.              + cTexCoords*2*sizeof(float); //texture coords
  941.  
  942.     pbCur = pbData = new BYTE[cbSize];
  943.     if (pbData == NULL)
  944.     {
  945.         hr = E_OUTOFMEMORY;
  946.         goto e_Exit;
  947.     }
  948.  
  949.     WRITE_DWORD(pbCur, cTexCoords); //nTextureCoords
  950.  
  951.     for (iVertex = 0; iVertex < pMeshData->m_cVertices; iVertex++)
  952.     {
  953.         iTextureIndex  = pMeshData->m_rgVertices[iVertex].iTextureIndex;
  954.         iMaterial = pMeshData->m_rgVertices[iVertex].iMaterial;
  955.         if( iTextureIndex == 0xFFFFFFFF ) // none present, or bad data
  956.         {
  957.             WRITE_FLOAT(pbCur, 0); //u
  958.             WRITE_FLOAT(pbCur, 0); //v
  959.         }
  960.         else
  961.         {
  962.             fX = pMesh->tVerts[iTextureIndex].x;
  963.             fY = (1.0f - pMesh->tVerts[iTextureIndex].y);
  964.  
  965.             fX = (fX > 1.0f) ? 1.0f : ((fX < 0.0f) ? 0.0f : fX);
  966.             fY = (fY > 1.0f) ? 1.0f : ((fY < 0.0f) ? 0.0f : fY);
  967.  
  968.             fX = (fX * rgCropInfo[iMaterial].fClipW) + rgCropInfo[iMaterial].fClipU;
  969.             fY = (fY * rgCropInfo[iMaterial].fClipH) + rgCropInfo[iMaterial].fClipV;
  970.  
  971.             WRITE_FLOAT(pbCur, fX); //u
  972.             WRITE_FLOAT(pbCur, fY); //v
  973.         }
  974.     }
  975.  
  976.     hr = psc->m_pxofsave->CreateDataObject(TID_D3DRMMeshTextureCoords,
  977.                                     NULL,
  978.                                     NULL,
  979.                                     cbSize,
  980.                                     pbData,
  981.                                     &pDataObject
  982.                                     );
  983.     if (FAILED(hr))
  984.     {
  985.         OutputDebugString("Failed to create x file data object!");
  986.         goto e_Exit;
  987.     }
  988.  
  989.     hr = pParent->AddDataObject(pDataObject);
  990.     if (FAILED(hr))
  991.     {
  992.         OutputDebugString("Failed to add x file data object!");
  993.         goto e_Exit;
  994.     }
  995.  
  996.     // falling through
  997. e_Exit:
  998.     RELEASE(pDataObject);
  999.  
  1000.     delete []pbData;
  1001.     return hr;
  1002. }
  1003.  
  1004. HRESULT AddPatchTextureCoordinates
  1005.     (
  1006.     SSaveContext *psc,
  1007.     SPatchMeshData* pPatchMeshData, 
  1008.     PatchMesh* pPatchMesh,
  1009.     LPDIRECTXFILEDATA pParent
  1010.     )
  1011. {
  1012.     LPDIRECTXFILEDATA pDataObject = NULL;
  1013.     PBYTE         pbData = NULL;
  1014.     PBYTE         pbCur = NULL;
  1015.     DWORD         cbSize;
  1016.     DWORD         cTexCoords;
  1017.     DWORD         iVertex;
  1018.     DWORD         iTextureIndex;
  1019.     HRESULT    hr = S_OK;
  1020.  
  1021.     assert( psc );
  1022.     assert( pParent );
  1023.     assert( pPatchMesh );
  1024.     assert( pPatchMeshData );
  1025.  
  1026.     // if no tex coords, then don't add them
  1027.     if( !pPatchMeshData->m_bTexCoordsPresent )
  1028.         goto e_Exit;
  1029.  
  1030.     cTexCoords = pPatchMeshData->m_cVertices;
  1031.  
  1032.     cbSize = sizeof(DWORD) //nTextureCoords
  1033.              + cTexCoords*2*sizeof(float); //texture coords
  1034.  
  1035.     pbCur = pbData = new BYTE[cbSize];
  1036.     if (pbData == NULL)
  1037.     {
  1038.         hr = E_OUTOFMEMORY;
  1039.         goto e_Exit;
  1040.     }
  1041.  
  1042.     WRITE_DWORD(pbCur, cTexCoords); //nTextureCoords
  1043.  
  1044.     for (iVertex = 0; iVertex < pPatchMeshData->m_cVertices; iVertex++)
  1045.     {
  1046.         iTextureIndex  = pPatchMeshData->m_rgVertices[iVertex].iTextureIndex;
  1047.         if( iTextureIndex == 0xFFFFFFFF ) // none present, or bad data
  1048.         {
  1049.             WRITE_FLOAT(pbCur, 0); //u
  1050.             WRITE_FLOAT(pbCur, 0); //v
  1051.         }
  1052.         else
  1053.         {
  1054.             WRITE_FLOAT(pbCur, pPatchMesh->tVerts[1][iTextureIndex].p.x); //u // 29 jan 01 : rjc - changed from .x to .p.x for UVVert p
  1055.             WRITE_FLOAT(pbCur, pPatchMesh->tVerts[1][iTextureIndex].p.y * -1); //v // 29 jan 01 : rjc - changed from .y to .p.y for UVVert p
  1056.         }
  1057.     }
  1058.  
  1059.     hr = psc->m_pxofsave->CreateDataObject(TID_D3DRMMeshTextureCoords,
  1060.                                     NULL,
  1061.                                     NULL,
  1062.                                     cbSize,
  1063.                                     pbData,
  1064.                                     &pDataObject
  1065.                                     );
  1066.     if (FAILED(hr))
  1067.     {
  1068.         OutputDebugString("Failed to create x file data object!");
  1069.         goto e_Exit;
  1070.     }
  1071.  
  1072.     hr = pParent->AddDataObject(pDataObject);
  1073.     if (FAILED(hr))
  1074.     {
  1075.         OutputDebugString("Failed to add x file data object!");
  1076.         goto e_Exit;
  1077.     }
  1078.  
  1079.     // falling through
  1080. e_Exit:
  1081.     RELEASE(pDataObject);
  1082.  
  1083.     delete []pbData;
  1084.     return hr;
  1085. }
  1086.  
  1087. HRESULT AddVertexDuplicationIndices
  1088.     (
  1089.     SSaveContext *psc,
  1090.     SMeshData *pMeshData, 
  1091.     LPDIRECTXFILEDATA pParent
  1092.     )
  1093. {
  1094.     HRESULT    hr = S_OK;
  1095.     LPDIRECTXFILEDATA pDataObject = NULL;
  1096.     PBYTE         pbData = NULL;
  1097.     PBYTE         pbCur = NULL;
  1098.     DWORD         cbSize;
  1099.     DWORD         cIndices;
  1100.     DWORD         cVerticesBeforeDuplication;
  1101.     DWORD         iVertex;
  1102.  
  1103.     assert(psc != NULL);
  1104.     assert(pParent != NULL);
  1105.     assert(pMeshData != NULL);
  1106.  
  1107.     cIndices = pMeshData->m_cVertices;
  1108.     cVerticesBeforeDuplication = pMeshData->m_cVerticesBeforeDuplication;
  1109.  
  1110.     // if no new vertices, then don't add a record to the file
  1111.     if (pMeshData->m_cVerticesBeforeDuplication == pMeshData->m_cVertices)
  1112.         goto e_Exit;
  1113.  
  1114.     cbSize = sizeof(DWORD) //nIndices
  1115.              + sizeof(DWORD) //nVerticesBeforeDuplication
  1116.              + cIndices*sizeof(DWORD); // array of indices
  1117.  
  1118.     pbCur = pbData = new BYTE[cbSize];
  1119.     if (pbData == NULL)
  1120.     {
  1121.         hr = E_OUTOFMEMORY;
  1122.         goto e_Exit;
  1123.     }
  1124.  
  1125.     WRITE_DWORD(pbCur, cIndices) // nIndices;
  1126.     WRITE_DWORD(pbCur, cVerticesBeforeDuplication) // nVerticesBeforeDuplication
  1127.     
  1128.     for (iVertex = 0; iVertex < cIndices; iVertex++)
  1129.     {
  1130.         WRITE_DWORD(pbCur, pMeshData->m_rgVertices[iVertex].iPointRep); // index to original vertex without duplication.
  1131.     }
  1132.  
  1133.     hr = psc->m_pxofsave->CreateDataObject(DXFILEOBJ_VertexDuplicationIndices,
  1134.                                     NULL,
  1135.                                     NULL,
  1136.                                     cbSize,
  1137.                                     pbData,
  1138.                                     &pDataObject
  1139.                                     );
  1140.     if (FAILED(hr))
  1141.     {
  1142.         OutputDebugString("Failed to create x file data object!");
  1143.         goto e_Exit;
  1144.     }
  1145.  
  1146.     hr = pParent->AddDataObject(pDataObject);
  1147.     if (FAILED(hr))
  1148.     {
  1149.         OutputDebugString("Failed to add x file data object!");
  1150.         goto e_Exit;
  1151.     }
  1152.  
  1153.     // falling through
  1154. e_Exit:
  1155.     RELEASE(pDataObject);
  1156.  
  1157.     delete []pbData;
  1158.     return hr;
  1159. }
  1160.  
  1161. HRESULT AddPatchVertexDuplicationIndices
  1162.     (
  1163.     SSaveContext *psc,
  1164.     SPatchMeshData *pPatchMeshData, 
  1165.     LPDIRECTXFILEDATA pParent
  1166.     )
  1167. {
  1168.     HRESULT    hr = S_OK;
  1169.     LPDIRECTXFILEDATA pDataObject = NULL;
  1170.     PBYTE         pbData = NULL;
  1171.     PBYTE         pbCur = NULL;
  1172.     DWORD         cbSize;
  1173.     DWORD         cIndices;
  1174.     DWORD         cVerticesBeforeDuplication;
  1175.     DWORD         iVertex;
  1176.  
  1177.     assert(psc != NULL);
  1178.     assert(pParent != NULL);
  1179.     assert(pPatchMeshData != NULL);
  1180.  
  1181.     cIndices = pPatchMeshData->m_cVertices;
  1182.     cVerticesBeforeDuplication = pPatchMeshData->m_cVerticesBeforeDuplication;
  1183.  
  1184.     // if no new vertices, then don't add a record to the file
  1185.     if (pPatchMeshData->m_cVerticesBeforeDuplication == pPatchMeshData->m_cVertices)
  1186.         goto e_Exit;
  1187.  
  1188.     cbSize = sizeof(DWORD) //nIndices
  1189.              + sizeof(DWORD) //nVerticesBeforeDuplication
  1190.              + cIndices*sizeof(DWORD); // array of indices
  1191.  
  1192.     pbCur = pbData = new BYTE[cbSize];
  1193.     if (pbData == NULL)
  1194.     {
  1195.         hr = E_OUTOFMEMORY;
  1196.         goto e_Exit;
  1197.     }
  1198.  
  1199.     WRITE_DWORD(pbCur, cIndices) // nIndices;
  1200.     WRITE_DWORD(pbCur, cVerticesBeforeDuplication) // nVerticesBeforeDuplication
  1201.     
  1202.     for (iVertex = 0; iVertex < cIndices; iVertex++)
  1203.     {
  1204.         WRITE_DWORD(pbCur, pPatchMeshData->m_rgVertices[iVertex].iPointRep); // index to original vertex without duplication.
  1205.     }
  1206.  
  1207.     hr = psc->m_pxofsave->CreateDataObject(DXFILEOBJ_VertexDuplicationIndices,
  1208.                                     NULL,
  1209.                                     NULL,
  1210.                                     cbSize,
  1211.                                     pbData,
  1212.                                     &pDataObject
  1213.                                     );
  1214.     if (FAILED(hr))
  1215.     {
  1216.         OutputDebugString("Failed to create x file data object!");
  1217.         goto e_Exit;
  1218.     }
  1219.  
  1220.     hr = pParent->AddDataObject(pDataObject);
  1221.     if (FAILED(hr))
  1222.     {
  1223.         OutputDebugString("Failed to add x file data object!");
  1224.         goto e_Exit;
  1225.     }
  1226.  
  1227.     // falling through
  1228. e_Exit:
  1229.     RELEASE(pDataObject);
  1230.  
  1231.     delete []pbData;
  1232.     return hr;
  1233. }
  1234.  
  1235. HRESULT
  1236. AddWireframeMaterial(
  1237.     SSaveContext *psc,
  1238.     INode *pNode,
  1239.     LPDIRECTXFILEDATA pParent
  1240.     )
  1241. {
  1242.     HRESULT hr = S_OK;
  1243.     DWORD cbSize;
  1244.     LPDIRECTXFILEDATA pDataObject = NULL;
  1245.     PBYTE pbData = NULL;
  1246.     PBYTE pbCur;
  1247.     DWORD dwWFColor;
  1248.     D3DXCOLOR colorWF;
  1249.  
  1250.     cbSize = 4*sizeof(float) // colorRGBA
  1251.              + sizeof(float) //power
  1252.              + 3*sizeof(float) //specularColor
  1253.              + 3*sizeof(float); //emissiveColor
  1254.  
  1255.     pbData = pbCur = new BYTE[cbSize];
  1256.     if (pbCur == NULL)
  1257.     {
  1258.         hr = E_OUTOFMEMORY;
  1259.         goto e_Exit;
  1260.     }
  1261.     
  1262.     // get the wireframe color
  1263.     dwWFColor = pNode->GetWireColor();
  1264.     dwWFColor |= 0xff000000;  // set alpha to fully opaque
  1265.  
  1266.     // convert to floating point
  1267.     colorWF = D3DXCOLOR(dwWFColor);
  1268.  
  1269.     //RGBA
  1270.     WRITE_COLOR(pbCur, colorWF);
  1271.  
  1272.     // Wireframe doesn't have an explicit specular power, so output our default.
  1273.     WRITE_FLOAT(pbCur, POWER_DEFAULT);
  1274.  
  1275.     // Set the specular color identical to diffuse, as recommended in 3DSMax docs.
  1276.     WRITE_FLOAT(pbCur, colorWF.r);
  1277.     WRITE_FLOAT(pbCur, colorWF.g);
  1278.     WRITE_FLOAT(pbCur, colorWF.b);
  1279.  
  1280.     // Set the luminence to 0: the material isn't glowing.
  1281.     WRITE_FLOAT(pbCur, 0.0f);
  1282.     WRITE_FLOAT(pbCur, 0.0f);
  1283.     WRITE_FLOAT(pbCur, 0.0f);
  1284.  
  1285.     hr = psc->m_pxofsave->CreateDataObject(TID_D3DRMMaterial,
  1286.                                     NULL,
  1287.                                     NULL,
  1288.                                     cbSize,
  1289.                                     pbData,
  1290.                                     &pDataObject
  1291.                                     );
  1292.  
  1293.     hr = pParent->AddDataObject(pDataObject);
  1294.     if (FAILED(hr))
  1295.         goto e_Exit;
  1296.  
  1297. e_Exit:
  1298.     delete []pbData;
  1299.     RELEASE(pDataObject);
  1300.  
  1301.     return hr;
  1302. }
  1303.  
  1304. HRESULT
  1305. AddTextureFilename(
  1306.     SSaveContext *psc,
  1307.     TCHAR *szName,
  1308.     LPDIRECTXFILEDATA pParent
  1309.     )
  1310. {
  1311.     HRESULT hr = S_OK;
  1312.     LPDIRECTXFILEDATA pDataObject = NULL;
  1313.     DWORD cbSize;
  1314.  
  1315.     cbSize = sizeof(TCHAR**);
  1316.  
  1317.     hr = psc->m_pxofsave->CreateDataObject(TID_D3DRMTextureFilename,
  1318.                                     NULL,
  1319.                                     NULL,
  1320.                                     cbSize,
  1321.                                     &szName,
  1322.                                     &pDataObject
  1323.                                     );
  1324.     if (FAILED(hr))
  1325.         goto e_Exit;
  1326.  
  1327.     hr = pParent->AddDataObject(pDataObject);
  1328.     if (FAILED(hr))
  1329.         goto e_Exit;
  1330.  
  1331. e_Exit:
  1332.     RELEASE(pDataObject);
  1333.  
  1334.     return hr;
  1335. }
  1336.  
  1337. // ================================================== FindTextureFileName
  1338. // callback called by 3DSMAX
  1339. class FindTextureFilename : public NameEnumCallback
  1340. {
  1341. public:
  1342.     FindTextureFilename()
  1343.         :m_szTextureName(NULL) {}
  1344.  
  1345.     virtual void RecordName(TCHAR *szName)
  1346.     {
  1347.         m_szTextureName = szName;
  1348.     }
  1349.  
  1350.     TCHAR *m_szTextureName;    
  1351. };
  1352.  
  1353. HRESULT
  1354. AddMaterial(
  1355.     SSaveContext *psc,
  1356.     Mtl *pMtl,
  1357.     LPDIRECTXFILEDATA pParent
  1358.     )
  1359. {
  1360.     HRESULT hr = S_OK;
  1361.     LPDIRECTXFILEDATA pDataObject = NULL;
  1362.     PBYTE pbData = NULL;
  1363.     PBYTE pbCur;
  1364.     DWORD cbSize;
  1365.     TCHAR *szNiceFilename;
  1366.     Texmap *pTexMap;
  1367.     FindTextureFilename findTextureFilename;
  1368.     BOOL bDetailMap = FALSE;
  1369.  
  1370.     cbSize = 4*sizeof(float) // colorRGBA
  1371.              + sizeof(float) //power
  1372.              + 3*sizeof(float) //specularColor
  1373.              + 3*sizeof(float); //emissiveColor
  1374.  
  1375.     pbData = pbCur = new BYTE[cbSize];
  1376.     if (pbCur == NULL)
  1377.     {
  1378.         hr = E_OUTOFMEMORY;
  1379.         goto e_Exit;
  1380.     }
  1381.  
  1382.     pTexMap = pMtl->GetSubTexmap(ID_DI);
  1383.     if ((pTexMap != NULL) && (pTexMap->ClassID() == Class_ID(BMTEX_CLASS_ID, 0x00)))
  1384.     {
  1385.         bDetailMap = TRUE;
  1386.     }
  1387.     
  1388.     // 'Ambient color' is specified in Max docs as "the color of the object in shadows," and 
  1389.     //                 is usually a darker version of the diffuse color.
  1390.     // 'Diffuse color' is specified in the Max docs as "the color of the object in 
  1391.     //                  good lighting," usually referred to as the objects' color.
  1392.     // 'Specular color' is specified as the color of reflection highlights in direct lighting,
  1393.     //                  and according to Max docs is usually the same as diffuse.
  1394.  
  1395.     if (!bDetailMap)
  1396.     {
  1397.         // We're going to use the 'Diffuse' color as the object color for DirectX
  1398.         WRITE_POINT3(pbCur, pMtl->GetDiffuse());
  1399.         //Alpha
  1400.         WRITE_FLOAT(pbCur, 1 - pMtl->GetXParency());
  1401.  
  1402.         // 3DSMax specular power is comprised of shininess, and shininess strength.
  1403.         // TODO - figure out a mapping from shininess to power
  1404.         WRITE_FLOAT(pbCur, POWER_DEFAULT);
  1405.  
  1406.         // Specular
  1407.         WRITE_POINT3(pbCur, pMtl->GetSpecular());
  1408.  
  1409.         // Emmissive
  1410.         WRITE_POINT3(pbCur, pMtl->GetSelfIllumColor());
  1411.  
  1412.     }
  1413.     else  // if a detail map, then don't write the color
  1414.     {
  1415.         // diffuse - write white RGBA
  1416.         WRITE_FLOAT(pbCur, 1.0f);
  1417.         WRITE_FLOAT(pbCur, 1.0f);
  1418.         WRITE_FLOAT(pbCur, 1.0f);
  1419.         WRITE_FLOAT(pbCur, 1.0f);
  1420.  
  1421.         // specular power
  1422.         WRITE_FLOAT(pbCur, POWER_DEFAULT);
  1423.  
  1424.         // specular - write white RGBA
  1425.         WRITE_FLOAT(pbCur, 1.0f);
  1426.         WRITE_FLOAT(pbCur, 1.0f);
  1427.         WRITE_FLOAT(pbCur, 1.0f);
  1428.  
  1429.         // emmissive - write white RGBA
  1430.         WRITE_FLOAT(pbCur, 0.0f);
  1431.         WRITE_FLOAT(pbCur, 0.0f);
  1432.         WRITE_FLOAT(pbCur, 0.0f);
  1433.     }
  1434.  
  1435.     hr = psc->m_pxofsave->CreateDataObject(TID_D3DRMMaterial,
  1436.                                     NULL,
  1437.                                     NULL,
  1438.                                     cbSize,
  1439.                                     pbData,
  1440.                                     &pDataObject
  1441.                                     );
  1442.     if (FAILED(hr))
  1443.         goto e_Exit;
  1444.  
  1445.     // See if there is a valid bitmap texture
  1446.     if (bDetailMap)
  1447.     {
  1448.         pTexMap->EnumAuxFiles(findTextureFilename, FILE_ENUM_ALL);
  1449.  
  1450.         if (findTextureFilename.m_szTextureName == NULL)
  1451.         {
  1452.             OutputDebugString("AddMaterial: Bitmap texture found, but no texture name.  Skipping texture name\n");
  1453.             goto e_NoDetailMap;
  1454.         }
  1455.  
  1456.         if (psc->m_xFormat == DXFILEFORMAT_TEXT)
  1457.         {
  1458.             szNiceFilename = psc->m_stStrings.CreateNiceFilename(findTextureFilename.m_szTextureName); /*expand \ char to \\ */
  1459.             if (szNiceFilename == NULL)
  1460.             {
  1461.                 hr = E_OUTOFMEMORY;
  1462.                 goto e_Exit;
  1463.             }
  1464.  
  1465.  
  1466.             hr = AddTextureFilename(psc, szNiceFilename, pDataObject);
  1467.             if (FAILED(hr))
  1468.                 goto e_Exit;
  1469.         }
  1470.         else
  1471.         {
  1472.             hr = AddTextureFilename(psc, findTextureFilename.m_szTextureName, pDataObject);
  1473.             if (FAILED(hr))
  1474.                 goto e_Exit;
  1475.         }
  1476.     }
  1477. e_NoDetailMap:
  1478.  
  1479.     hr = pParent->AddDataObject(pDataObject);
  1480.     if (FAILED(hr))
  1481.         goto e_Exit;
  1482.  
  1483. e_Exit:
  1484.     delete []pbData;
  1485.     RELEASE(pDataObject);
  1486.  
  1487.     return hr;
  1488. }
  1489.  
  1490. HRESULT
  1491. AddMaterial(
  1492.     SSaveContext *psc,
  1493.     D3DXMATERIAL *pMaterial,
  1494.     LPDIRECTXFILEDATA pParent
  1495.     )
  1496. {
  1497.     HRESULT hr = S_OK;
  1498.     LPDIRECTXFILEDATA pDataObject = NULL;
  1499.     PBYTE pbData = NULL;
  1500.     PBYTE pbCur;
  1501.     DWORD cbSize;
  1502.  
  1503.     cbSize = 4*sizeof(float) // colorRGBA
  1504.              + sizeof(float) //power
  1505.              + 3*sizeof(float) //specularColor
  1506.              + 3*sizeof(float); //emissiveColor
  1507.  
  1508.     pbData = pbCur = new BYTE[cbSize];
  1509.     if (pbCur == NULL)
  1510.     {
  1511.         hr = E_OUTOFMEMORY;
  1512.         goto e_Exit;
  1513.     }
  1514.     
  1515.     // diffuse - write white RGBA
  1516.     WRITE_FLOAT(pbCur, pMaterial->MatD3D.Diffuse.r);
  1517.     WRITE_FLOAT(pbCur, pMaterial->MatD3D.Diffuse.g);
  1518.     WRITE_FLOAT(pbCur, pMaterial->MatD3D.Diffuse.b);
  1519.     WRITE_FLOAT(pbCur, pMaterial->MatD3D.Diffuse.a);
  1520.  
  1521.     // specular power
  1522.     WRITE_FLOAT(pbCur, POWER_DEFAULT);
  1523.  
  1524.     // specular - write white RGBA
  1525.     WRITE_FLOAT(pbCur, pMaterial->MatD3D.Specular.r);
  1526.     WRITE_FLOAT(pbCur, pMaterial->MatD3D.Specular.g);
  1527.     WRITE_FLOAT(pbCur, pMaterial->MatD3D.Specular.b);
  1528.  
  1529.     // emmissive - write white RGBA
  1530.     WRITE_FLOAT(pbCur, pMaterial->MatD3D.Emissive.r);
  1531.     WRITE_FLOAT(pbCur, pMaterial->MatD3D.Emissive.g);
  1532.     WRITE_FLOAT(pbCur, pMaterial->MatD3D.Emissive.b);
  1533.  
  1534.     hr = psc->m_pxofsave->CreateDataObject(TID_D3DRMMaterial,
  1535.                                     NULL,
  1536.                                     NULL,
  1537.                                     cbSize,
  1538.                                     pbData,
  1539.                                     &pDataObject
  1540.                                     );
  1541.     if (FAILED(hr))
  1542.         goto e_Exit;
  1543.  
  1544.     // if there is a bitmap texture, then add the filename
  1545.     if (pMaterial->pTextureFilename != NULL)
  1546.     {
  1547.         hr = AddTextureFilename(psc, pMaterial->pTextureFilename, pDataObject);
  1548.         if (FAILED(hr))
  1549.             goto e_Exit;
  1550.     }
  1551.  
  1552.     hr = pParent->AddDataObject(pDataObject);
  1553.     if (FAILED(hr))
  1554.         goto e_Exit;
  1555.  
  1556. e_Exit:
  1557.     delete []pbData;
  1558.     RELEASE(pDataObject);
  1559.  
  1560.     return hr;
  1561. }
  1562.  
  1563. HRESULT
  1564. GatherMeshMaterials(
  1565.     SSaveContext *psc,
  1566.     INode *pNode,
  1567.     Mesh *pMesh,
  1568.     DWORD **prgdwMeshMaterials,
  1569.     D3DXMATERIAL **prgMaterials,
  1570.     SCropInfo **prgCropInfo,
  1571.     DWORD *pcMaterials
  1572.     )
  1573. {
  1574.     HRESULT hr = S_OK;
  1575.     Mtl *pMtlMain;
  1576.     Mtl *pMtlCur;
  1577.     DWORD iCurMaterial;
  1578.     DWORD cSubMaterials;
  1579.     DWORD iFace;
  1580.     DWORD cFaces;
  1581.     BOOL bNoSubMaterials;
  1582.     BOOL bWireframeColor;
  1583.     DWORD dwCurMatID;
  1584.     DWORD *rgdwMeshMaterials = NULL;
  1585.     SCropInfo *rgCropInfo = NULL;
  1586.     IParamBlock2 *pParamBlock;
  1587.     Texmap *pTexMap;
  1588.     D3DXMATERIAL *rgMaterials = NULL;
  1589.     TCHAR *szFilename;
  1590.     DWORD dwWFColor;
  1591.     D3DXCOLOR colorWF;
  1592.     BOOL bDetailMap;
  1593.     FindTextureFilename findTextureFilename;
  1594.  
  1595.     pMtlMain = pNode->GetMtl();
  1596.     cFaces = pMesh->getNumFaces();
  1597.     cSubMaterials = 0;
  1598.     bNoSubMaterials = FALSE;
  1599.     bWireframeColor = FALSE;
  1600.  
  1601.     rgdwMeshMaterials = new DWORD[cFaces];
  1602.     if (rgdwMeshMaterials == NULL)
  1603.     {
  1604.         hr = E_OUTOFMEMORY;
  1605.         goto e_Exit;
  1606.     }
  1607.  
  1608.     // The color of a given mesh can be provided by three different sources:
  1609.     //   1) applied texture maps, as part of a material
  1610.     //   2) explicitly defined & applied materials without texture maps
  1611.     //   3) a 'wireframe' color.
  1612.     
  1613.     // For our purposes, we will output these in this order of preference, ignoring
  1614.     // processing past the first one we find.
  1615.  
  1616.     if (pMtlMain != NULL)
  1617.     {
  1618.         // There is at least one material. We're in case 1) or 2)
  1619.  
  1620.         cSubMaterials = pMtlMain->NumSubMtls();
  1621.         if (cSubMaterials < 1)
  1622.         {
  1623.             // Count the material itself as a submaterial.
  1624.             cSubMaterials = 1;
  1625.             bNoSubMaterials = TRUE;
  1626.         }
  1627.     }
  1628.     else  // no material, then we'll create a material corresponding to the default wire color.
  1629.     {
  1630.         bWireframeColor = TRUE;
  1631.         cSubMaterials = 1;
  1632.     }
  1633.  
  1634.     for (iFace=0; iFace < cFaces; iFace++)
  1635.     {
  1636.         if (bWireframeColor || bNoSubMaterials) 
  1637.         {
  1638.             // If we're using wireframe color, it's our only material
  1639.             rgdwMeshMaterials[iFace] = 0;
  1640.         }
  1641.         else
  1642.         {
  1643.             // Otherwise we have at least one material to process.
  1644.  
  1645.             dwCurMatID = pMesh->faces[iFace].getMatID();
  1646.  
  1647.             if (cSubMaterials == 1)
  1648.             {
  1649.                 dwCurMatID = 0;
  1650.             }
  1651.             else
  1652.             {
  1653.                 // SDK recommends mod'ing the material ID by the valid # of materials, 
  1654.                 // as sometimes a material number that's too high is returned.
  1655.                 dwCurMatID %= cSubMaterials;
  1656.             }
  1657.  
  1658.             // output the appropriate material color
  1659.  
  1660.             rgdwMeshMaterials[iFace] = dwCurMatID;
  1661.  
  1662.         } 
  1663.  
  1664.     } 
  1665.  
  1666.     rgCropInfo = new SCropInfo[cSubMaterials];
  1667.     rgMaterials = new D3DXMATERIAL[cSubMaterials];
  1668.     if ((rgCropInfo == NULL) || (rgMaterials == NULL))
  1669.     {
  1670.         hr = E_OUTOFMEMORY;
  1671.         goto e_Exit;
  1672.     }
  1673.     memset(rgMaterials, 0, sizeof(D3DXMATERIAL) * cSubMaterials);
  1674.  
  1675.     for (iCurMaterial = 0; iCurMaterial < cSubMaterials; iCurMaterial++)
  1676.     {
  1677.         rgCropInfo[iCurMaterial].fClipH = 0.0f;
  1678.         rgCropInfo[iCurMaterial].fClipV = 0.0f;
  1679.         rgCropInfo[iCurMaterial].fClipW = 1.0f;
  1680.         rgCropInfo[iCurMaterial].fClipH = 1.0f;
  1681.     }
  1682.  
  1683.     if (!bWireframeColor)
  1684.     {
  1685.         // 3DSMax allows multiple materials to be used on a single mesh via
  1686.         // 'submaterials'. When the first submaterial is defined, the main material
  1687.         // is copied into submaterial #1, and the new submaterial because submaterial #2.
  1688.         // 
  1689.         // We have to catch the case where there's a material, but no submaterials. For this
  1690.         // case, set NumSubMaterials to 1 which would never happen otherwise. It's imperative
  1691.         // that the first material be set to MtlMain, rather than trying to GetSubMtl() to 
  1692.         // allow this logic to work.
  1693.  
  1694.         // Loop through the materials (if any) and output them.
  1695.  
  1696.         // first collect the crop info and the list of materials
  1697.         for (iCurMaterial = 0; iCurMaterial < cSubMaterials; iCurMaterial++)
  1698.         {
  1699.             if (bNoSubMaterials)
  1700.             {
  1701.                 // We're on the parent material, possibly the ONLY material.
  1702.                 // We won't be able to get it with GetSubMtl() if it's the only one, and
  1703.                 // the data in the first submaterial is identical to the main material,
  1704.                 // so just use the main material.
  1705.  
  1706.                 pMtlCur = pMtlMain;
  1707.             }
  1708.             else
  1709.             {
  1710.                 // We're into the true submaterials. Safe to get with 'GetSubMtl'
  1711.  
  1712.                 pMtlCur = pMtlMain->GetSubMtl(iCurMaterial);
  1713.             }
  1714.  
  1715.             // if using texture as a detail map, get the crop info
  1716.             bDetailMap = FALSE;
  1717.             pTexMap = pMtlCur->GetSubTexmap(ID_DI);
  1718.             if ((pTexMap != NULL) && (pTexMap->ClassID() == Class_ID(BMTEX_CLASS_ID, 0x00)))
  1719.             {
  1720.                 pParamBlock = pTexMap->GetParamBlock(0);
  1721.  
  1722.                 if (pParamBlock != NULL)
  1723.                 {
  1724.                     pParamBlock->GetValue(bmtex_clipu, 0, rgCropInfo[iCurMaterial].fClipU, FOREVER);
  1725.                     pParamBlock->GetValue(bmtex_clipv, 0, rgCropInfo[iCurMaterial].fClipV, FOREVER);
  1726.                     pParamBlock->GetValue(bmtex_clipw, 0, rgCropInfo[iCurMaterial].fClipW, FOREVER);
  1727.                     pParamBlock->GetValue(bmtex_cliph, 0, rgCropInfo[iCurMaterial].fClipH, FOREVER);       
  1728.                 }
  1729.  
  1730.                 bDetailMap = TRUE;
  1731.             }
  1732.  
  1733.             // 'Ambient color' is specified in Max docs as "the color of the object in shadows," and 
  1734.             //                 is usually a darker version of the diffuse color.
  1735.             // 'Diffuse color' is specified in the Max docs as "the color of the object in 
  1736.             //                  good lighting," usually referred to as the objects' color.
  1737.             // 'Specular color' is specified as the color of reflection highlights in direct lighting,
  1738.             //                  and according to Max docs is usually the same as diffuse.
  1739.  
  1740.             if (!bDetailMap)
  1741.             {
  1742.                 // We're going to use the 'Diffuse' color as the object color for DirectX
  1743.                 rgMaterials[iCurMaterial].MatD3D.Diffuse.r = pMtlCur->GetDiffuse().r;
  1744.                 rgMaterials[iCurMaterial].MatD3D.Diffuse.g = pMtlCur->GetDiffuse().g;
  1745.                 rgMaterials[iCurMaterial].MatD3D.Diffuse.b = pMtlCur->GetDiffuse().b;
  1746.  
  1747.                 //Alpha
  1748.                 rgMaterials[iCurMaterial].MatD3D.Diffuse.a = 1.0f - pMtlCur->GetXParency();
  1749.  
  1750.                 // 3DSMax specular power is comprised of shininess, and shininess strength.
  1751.                 // TODO - figure out a mapping from shininess to power
  1752.                 rgMaterials[iCurMaterial].MatD3D.Power = POWER_DEFAULT;
  1753.  
  1754.                 // Specular
  1755.                 rgMaterials[iCurMaterial].MatD3D.Specular.r = pMtlCur->GetSpecular().r;
  1756.                 rgMaterials[iCurMaterial].MatD3D.Specular.g = pMtlCur->GetSpecular().g;
  1757.                 rgMaterials[iCurMaterial].MatD3D.Specular.b = pMtlCur->GetSpecular().b;
  1758.                 rgMaterials[iCurMaterial].MatD3D.Specular.a = 1.0f;
  1759.  
  1760.                 // Emmissive
  1761.                 rgMaterials[iCurMaterial].MatD3D.Emissive.r = pMtlCur->GetSelfIllumColor().r;
  1762.                 rgMaterials[iCurMaterial].MatD3D.Emissive.g = pMtlCur->GetSelfIllumColor().g;
  1763.                 rgMaterials[iCurMaterial].MatD3D.Emissive.b = pMtlCur->GetSelfIllumColor().b;
  1764.                 rgMaterials[iCurMaterial].MatD3D.Emissive.a = 1.0f;
  1765.             }
  1766.             else  // if a detail map, then don't write the color
  1767.             {
  1768.                 // diffuse - write white RGBA
  1769.                 rgMaterials[iCurMaterial].MatD3D.Diffuse.r = 1.0f;
  1770.                 rgMaterials[iCurMaterial].MatD3D.Diffuse.g = 1.0f;
  1771.                 rgMaterials[iCurMaterial].MatD3D.Diffuse.b = 1.0f;
  1772.                 rgMaterials[iCurMaterial].MatD3D.Diffuse.a = 1.0f;
  1773.  
  1774.                 rgMaterials[iCurMaterial].MatD3D.Power = POWER_DEFAULT;
  1775.  
  1776.                 // specular - write white RGBA
  1777.                 rgMaterials[iCurMaterial].MatD3D.Specular.r = 1.0f;
  1778.                 rgMaterials[iCurMaterial].MatD3D.Specular.g = 1.0f;
  1779.                 rgMaterials[iCurMaterial].MatD3D.Specular.b = 1.0f;
  1780.                 rgMaterials[iCurMaterial].MatD3D.Specular.a = 1.0f;
  1781.  
  1782.                 // emmissive - write white RGBA
  1783.                 rgMaterials[iCurMaterial].MatD3D.Emissive.r = 0.0f;
  1784.                 rgMaterials[iCurMaterial].MatD3D.Emissive.g = 0.0f;
  1785.                 rgMaterials[iCurMaterial].MatD3D.Emissive.b = 0.0f;
  1786.                 rgMaterials[iCurMaterial].MatD3D.Emissive.a = 1.0f;
  1787.             }
  1788.  
  1789.             if (bDetailMap)
  1790.             {
  1791.                 pTexMap->EnumAuxFiles(findTextureFilename, FILE_ENUM_ALL);
  1792.  
  1793.                 if (findTextureFilename.m_szTextureName == NULL)
  1794.                 {
  1795.                     OutputDebugString("AddMaterial: Bitmap texture found, but no texture name\n");
  1796.                     szFilename = NULL;
  1797.                 }
  1798.                 else // texture filename found
  1799.                 {
  1800.                     // allocate a new string, doing the '\' fixup if neccessary
  1801.                     if (psc->m_xFormat == DXFILEFORMAT_TEXT)
  1802.                         szFilename = psc->m_stStrings.CreateNiceFilename(findTextureFilename.m_szTextureName); /*expand \ char to \\ */
  1803.                     else
  1804.                         szFilename = psc->m_stStrings.CreateNormalFilename(findTextureFilename.m_szTextureName); /*DON'T! expand \ char to \\ */
  1805.  
  1806.                     if (szFilename == NULL)
  1807.                     {
  1808.                         hr = E_OUTOFMEMORY;
  1809.                         goto e_Exit;
  1810.                     }
  1811.                 }
  1812.  
  1813.                 rgMaterials[iCurMaterial].pTextureFilename = szFilename;
  1814.             }
  1815.         } 
  1816.     }
  1817.     else // wireframe color
  1818.     {
  1819.        // get the wireframe color
  1820.         dwWFColor = pNode->GetWireColor();
  1821.         dwWFColor |= 0xff000000;  // set alpha to fully opaque
  1822.  
  1823.         // convert to floating point
  1824.         colorWF = D3DXCOLOR(dwWFColor);
  1825.  
  1826.         //RGBA
  1827.         rgMaterials[0].MatD3D.Diffuse.r = colorWF.r;
  1828.         rgMaterials[0].MatD3D.Diffuse.g = colorWF.g;
  1829.         rgMaterials[0].MatD3D.Diffuse.b = colorWF.b;
  1830.         rgMaterials[0].MatD3D.Diffuse.a = 1.0f;
  1831.  
  1832.         // Wireframe doesn't have an explicit specular power, so output our default.
  1833.         rgMaterials[0].MatD3D.Power = POWER_DEFAULT;
  1834.  
  1835.         // Set the specular color identical to diffuse, as recommended in 3DSMax docs.
  1836.         rgMaterials[0].MatD3D.Specular.r = colorWF.r;
  1837.         rgMaterials[0].MatD3D.Specular.g = colorWF.g;
  1838.         rgMaterials[0].MatD3D.Specular.b = colorWF.b;
  1839.         rgMaterials[0].MatD3D.Specular.a = 1.0f;
  1840.  
  1841.         // Set the luminence to 0: the material isn't glowing.
  1842.         rgMaterials[0].MatD3D.Emissive.r = 0.0f;
  1843.         rgMaterials[0].MatD3D.Emissive.g = 0.0f;
  1844.         rgMaterials[0].MatD3D.Emissive.b = 0.0f;
  1845.         rgMaterials[0].MatD3D.Emissive.a = 1.0f;
  1846.  
  1847.     }
  1848.  
  1849.     *prgdwMeshMaterials = rgdwMeshMaterials;
  1850.     rgdwMeshMaterials = NULL;
  1851.  
  1852.     *prgCropInfo = rgCropInfo;
  1853.     rgCropInfo = NULL;
  1854.  
  1855.     *prgMaterials = rgMaterials;
  1856.     rgMaterials = NULL;
  1857.  
  1858.     *pcMaterials = cSubMaterials;
  1859.  
  1860. e_Exit:
  1861.     delete []rgdwMeshMaterials;
  1862.     delete []rgMaterials;
  1863.     delete []rgCropInfo;
  1864.     return hr;
  1865. }
  1866.  
  1867. HRESULT
  1868. AddMeshMaterials(
  1869.     SSaveContext *psc,
  1870.     INode *pNode,
  1871.     Mesh *pMesh,
  1872.     DWORD *rgdwMeshMaterials,
  1873.     D3DXMATERIAL *rgMaterials,
  1874.     DWORD cMaterials,
  1875.     LPDIRECTXFILEDATA pParent
  1876.     )
  1877. {
  1878.     HRESULT hr = S_OK;
  1879.     LPDIRECTXFILEDATA pDataObject = NULL;
  1880.     DWORD iFace;
  1881.     DWORD cFaces;
  1882.     PBYTE pbCur;
  1883.     PBYTE pbData = NULL;
  1884.     DWORD cbSize;
  1885.     DWORD iCurMaterial;
  1886.     BOOL bFound;
  1887.     DWORD cUniqueMaterials;
  1888.     DWORD iSearchMaterial;
  1889.     DWORD iPrevMaterial;
  1890.     DWORD iDestMaterial;
  1891.     DWORD *rgdwMaterialRemap = NULL;
  1892.  
  1893.     cFaces = pMesh->getNumFaces();
  1894.  
  1895.     rgdwMaterialRemap = new DWORD[cMaterials];
  1896.     if (rgdwMaterialRemap == NULL)
  1897.     {
  1898.         hr = E_OUTOFMEMORY;
  1899.         goto e_Exit;
  1900.     }
  1901.  
  1902.     // first go through and remove redundant materials (might not have had the same crop info!)
  1903.     cUniqueMaterials = 1;
  1904.     rgdwMaterialRemap[0] = 0;
  1905.     for (iCurMaterial = 1; iCurMaterial < cMaterials; iCurMaterial++)
  1906.     {
  1907.         bFound = FALSE;
  1908.         for (iSearchMaterial = 0; iSearchMaterial < iCurMaterial; iSearchMaterial++)
  1909.         {
  1910.             if (iSearchMaterial == iCurMaterial)
  1911.                 continue;
  1912.  
  1913.             // if both the material and texture name are the same, then it is a redundant material
  1914.             if ((memcmp(&(rgMaterials[iCurMaterial].MatD3D), &(rgMaterials[iSearchMaterial].MatD3D), sizeof(D3DMATERIAL8)) == 0)
  1915.                 && (strcmp(rgMaterials[iCurMaterial].pTextureFilename, rgMaterials[iSearchMaterial].pTextureFilename) == 0))
  1916.             {
  1917.                 bFound = TRUE;
  1918.                 break;
  1919.             }
  1920.  
  1921.         }
  1922.  
  1923.         // if found, just remap to the material that was found
  1924.         if (bFound)
  1925.         {
  1926.             rgdwMaterialRemap[iCurMaterial] = rgdwMaterialRemap[iSearchMaterial];
  1927.         }
  1928.         else  // not found, another unique material
  1929.         {
  1930.             rgdwMaterialRemap[iCurMaterial] = cUniqueMaterials;
  1931.             cUniqueMaterials += 1;
  1932.         }
  1933.     }
  1934.  
  1935.     // now remap the materials
  1936.     iPrevMaterial = 0;
  1937.     for (iCurMaterial = 1; iCurMaterial < cMaterials; iCurMaterial++)
  1938.     {
  1939.         iDestMaterial = rgdwMaterialRemap[iCurMaterial];
  1940.  
  1941.         // if a unique material, then move it
  1942.         if (iDestMaterial > iPrevMaterial)
  1943.         {
  1944.             iPrevMaterial += 1;
  1945.             assert(iDestMaterial == iPrevMaterial);
  1946.  
  1947.             // if not staying in the same place, then copy it
  1948.             if (iCurMaterial != iDestMaterial)
  1949.             {
  1950.                 memcpy(&rgMaterials[iDestMaterial], &rgMaterials[iCurMaterial], sizeof(D3DXMATERIAL));
  1951.             }
  1952.         }
  1953.     }
  1954.  
  1955.  
  1956.     cbSize = sizeof(DWORD) // nMaterials
  1957.                 + sizeof(DWORD) // nFaceIndexes
  1958.                 + cFaces*sizeof(DWORD); // face indexes
  1959.  
  1960.     pbData = pbCur = new BYTE[cbSize];
  1961.     if (pbCur == NULL)
  1962.     {
  1963.         hr = E_OUTOFMEMORY;
  1964.         goto e_Exit;
  1965.     }
  1966.  
  1967.     WRITE_DWORD(pbCur, cUniqueMaterials);
  1968.     WRITE_DWORD(pbCur, cFaces);
  1969.  
  1970.  
  1971.     // For each face, output the index of the material which applies to it, 
  1972.     // starting from  0
  1973.  
  1974.     for (iFace=0; iFace < cFaces; iFace++)
  1975.     {
  1976.         // don't forget to remap the matrerial before writing it to the file
  1977.         WRITE_DWORD(pbCur, rgdwMaterialRemap[rgdwMeshMaterials[iFace]];);
  1978.     } 
  1979.  
  1980.     // now finally create the mesh material list
  1981.     hr = psc->m_pxofsave->CreateDataObject(TID_D3DRMMeshMaterialList,
  1982.                                     NULL,
  1983.                                     NULL,
  1984.                                     cbSize,
  1985.                                     pbData,
  1986.                                     &pDataObject
  1987.                                     );
  1988.     if (FAILED(hr))
  1989.         goto e_Exit;
  1990.  
  1991.     hr = pParent->AddDataObject(pDataObject);
  1992.     if (FAILED(hr))
  1993.         goto e_Exit;
  1994.  
  1995.     for (iCurMaterial = 0; iCurMaterial < cUniqueMaterials; iCurMaterial++)
  1996.     {
  1997.         hr = AddMaterial(psc, &rgMaterials[iCurMaterial], pDataObject);
  1998.         if (FAILED(hr))
  1999.             goto e_Exit;
  2000.     } 
  2001.  
  2002. e_Exit:
  2003.     delete []pbData;
  2004.     delete []rgdwMaterialRemap;
  2005.     RELEASE(pDataObject);
  2006.  
  2007.  
  2008.     return hr;
  2009.  
  2010. // structe used by AddSkinData to convert skin data from per vertex to per bone
  2011. struct SBoneState
  2012. {
  2013.     SBoneState()
  2014.         :m_pbData(NULL), m_szBoneName(NULL) {}
  2015.     ~SBoneState()
  2016.         { 
  2017.             delete []m_pbData; 
  2018.  
  2019.             // Bone name is allocated out of a string pool because it has to be deleted after
  2020.             //   the data has been saved to disk
  2021.             //delete []m_szBoneName; 
  2022.         }
  2023.  
  2024.     // info computed up front (note m_rgdwIndices, m_rgfWeights and m_pmatOFfset point into pbData)
  2025.     INode *m_pBoneNode;
  2026.     DWORD m_cVertices;
  2027.     DWORD m_cbData;
  2028.     PBYTE m_pbData;
  2029.     char *m_szBoneName;
  2030.     DWORD *m_rgdwIndices;
  2031.     float *m_rgfWeights;
  2032.     D3DXMATRIX *m_pmatOffset;
  2033.  
  2034.     // current index of last added vertex index
  2035.     DWORD m_iCurIndex;
  2036. };
  2037.  
  2038. SBoneState *FindBoneData
  2039.     (
  2040.     INode *pBoneNode, 
  2041.     SBoneState *rgbsBoneData, 
  2042.     DWORD cBones
  2043.     )
  2044. {
  2045.     DWORD iBone;
  2046.     SBoneState *pbs = NULL;
  2047.  
  2048.     for (iBone = 0; iBone < cBones; iBone++)
  2049.     {
  2050.         if (rgbsBoneData[iBone].m_pBoneNode == pBoneNode)
  2051.         {
  2052.             pbs = &rgbsBoneData[iBone];
  2053.             break;
  2054.         }
  2055.     }
  2056.  
  2057.     return pbs;
  2058. }
  2059.  
  2060. HRESULT AddSkinData
  2061.     (
  2062.     SSaveContext *psc,
  2063.     INode *pNode,
  2064.     SMeshData *pMeshData,
  2065.     LPDIRECTXFILEDATA pParent
  2066.     )
  2067. {
  2068.     HRESULT hr = S_OK;
  2069.     IPhyVertexExport *pVertexExport;
  2070.     IPhyRigidVertex* pRigidVertexExport;
  2071.     IPhyBlendedRigidVertex *pBlendedRigidVertexExport;
  2072.     INode* pBoneNode;
  2073.     SSkinMap *psmSkinMap = NULL;
  2074.     Modifier* pPhyMod = NULL;
  2075.     IPhysiqueExport* pPhyExport = NULL;
  2076.     IPhyContextExport* pPhyContextExport = NULL;
  2077.     SBoneState *rgbsBoneData = NULL;
  2078.     SBoneState *rgbsBoneDataDups = NULL;
  2079.     SBoneState *pbsCurData = NULL;
  2080.     SBoneState *pbsOldData = NULL;
  2081.     DWORD iVertex;
  2082.     DWORD cVertices;
  2083.     DWORD iVertexType;
  2084.     DWORD cTotalBones;
  2085.     DWORD iBone;
  2086.     PBYTE pbCur;
  2087.     float fWeight;
  2088.     LPDIRECTXFILEDATA pDataObject = NULL;
  2089.     DWORD iIndex;
  2090.     DWORD iNextWedge;
  2091.     Matrix3 matMesh;
  2092.     Matrix3 matOffset;
  2093.     Matrix3 matZero;
  2094.     PBYTE pbHeaderData = NULL;
  2095.     DWORD cbHeaderData;
  2096.     static bool shownError = false;
  2097.  
  2098.     matZero.Zero();
  2099.     pPhyMod = FindPhysiqueModifier(pNode);
  2100.     if (pPhyMod != NULL)
  2101.     {
  2102.         // Get a Physique Export interface
  2103.         pPhyExport = (IPhysiqueExport *)pPhyMod->GetInterface(I_PHYINTERFACE);
  2104.         if (pPhyExport == NULL)
  2105.         {   // not all interfaces present, so ignore
  2106.             goto e_NoPhysique;
  2107.         }
  2108.  
  2109.         // get the init matrix for the mesh (used to mult by the bone matrices)
  2110.         int rval = pPhyExport->GetInitNodeTM(pNode, matMesh);
  2111.         if (rval || matMesh.Equals(matZero, 0.0))
  2112.         {
  2113.             if (!shownError)
  2114.             {
  2115.                 shownError = true;
  2116.                 MessageBox(NULL, "This mesh was skinned using an older version of Character Studio. Mesh may not be exported correctly",
  2117.                            "X File export Warning", MB_OK);
  2118.             }
  2119.         }
  2120.  
  2121.         // For a given Object's INode get a
  2122.         // ModContext Interface from the Physique Export Interface:
  2123.         pPhyContextExport = (IPhyContextExport *)pPhyExport->GetContextInterface(pNode);
  2124.         if(pPhyContextExport == NULL)
  2125.         {
  2126.             // not all interfaces present, so ignore
  2127.             goto e_NoPhysique;
  2128.         }
  2129.  
  2130.         // should already have been converted to rigid with blending by Preprocess
  2131.             // convert to rigid with blending
  2132.             pPhyContextExport->ConvertToRigid(TRUE);
  2133.             pPhyContextExport->AllowBlending(TRUE);
  2134.  
  2135.         psmSkinMap = psc->GetSkinMap(pNode);
  2136.         if (psmSkinMap == NULL)
  2137.         {
  2138.             OutputDebugString("AddSkinData: Found physique info at save time, but not preprocess\n");
  2139.             hr = E_FAIL;
  2140.             goto e_Exit;
  2141.         }
  2142.  
  2143.         // now setup state used to fill arrays for output 
  2144.         rgbsBoneData = new SBoneState[psmSkinMap->m_cbiBones];
  2145.         if (rgbsBoneData == NULL)
  2146.         {
  2147.             hr = E_OUTOFMEMORY;
  2148.             goto e_Exit;
  2149.         }
  2150.  
  2151.         // intialize each of the bone structures 
  2152.         for (iBone = 0; iBone < psmSkinMap->m_cbiBones; iBone++)
  2153.         {
  2154.             pbsCurData = &rgbsBoneData[iBone];
  2155.  
  2156.             pbsCurData->m_iCurIndex = 0;
  2157.             pbsCurData->m_cVertices = psmSkinMap->m_rgbiBones[iBone].m_cVertices;
  2158.             pbsCurData->m_pBoneNode = psmSkinMap->m_rgbiBones[iBone].m_pBoneNode;
  2159.  
  2160.             // allocate memory to pass to D3DXOF lib
  2161.             pbsCurData->m_cbData = sizeof(TCHAR*)
  2162.                                     + sizeof(DWORD) // numWeights
  2163.                                     + sizeof(DWORD) * pbsCurData->m_cVertices // array of vertex indices
  2164.                                     + sizeof(float) * pbsCurData->m_cVertices // array of weights
  2165.                                     + sizeof(float) * 16; // offset matrix
  2166.  
  2167.             pbCur = pbsCurData->m_pbData = new BYTE[pbsCurData->m_cbData];
  2168.             if (pbsCurData->m_pbData == NULL)
  2169.             {
  2170.                 hr = E_OUTOFMEMORY;
  2171.                 goto e_Exit;
  2172.             }
  2173.  
  2174.             // fill first few entries, and remember pointers to the other arrays/matrix
  2175.             WRITE_PTCHAR(pbCur, pbsCurData->m_szBoneName);
  2176.             WRITE_DWORD(pbCur, pbsCurData->m_cVertices);
  2177.  
  2178.             pbsCurData->m_rgdwIndices = (DWORD*)pbCur;
  2179.             pbCur += sizeof(DWORD) * pbsCurData->m_cVertices;
  2180.  
  2181.             pbsCurData->m_rgfWeights = (float*)pbCur;
  2182.             pbCur += sizeof(float) * pbsCurData->m_cVertices;
  2183.  
  2184.             pbsCurData->m_pmatOffset = (D3DXMATRIX*)pbCur;
  2185.  
  2186.             // compute the mat offset
  2187.             int rval = pPhyExport->GetInitNodeTM(pbsCurData->m_pBoneNode, matOffset);
  2188.             if (rval)
  2189.             {
  2190.                 MessageBox(NULL, "This mesh was skinned using an older version of Character Studio. Mesh may not be exported correctly",
  2191.                            "X File export Warning", MB_OK);
  2192.             }
  2193.             matOffset.Invert();
  2194.             matOffset = matMesh * matOffset;
  2195.  
  2196.             WRITE_MATRIX4_FROM_MATRIX3(pbCur, matOffset);
  2197.         }
  2198.  
  2199.         cVertices = pPhyContextExport->GetNumberVertices();
  2200.         for (iVertex = 0; iVertex < cVertices; iVertex++ )
  2201.         {
  2202.             pVertexExport = (IPhyVertexExport *)pPhyContextExport->GetVertexInterface(iVertex);    
  2203.             if (pVertexExport == NULL)
  2204.             {
  2205.                 OutputDebugString("Couldn't get export interface!");
  2206.                 hr = E_FAIL;
  2207.                 goto e_Exit;
  2208.             }
  2209.         
  2210.             // What kind of vertices are these?
  2211.             iVertexType = pVertexExport->GetVertexType();
  2212.  
  2213.             pPhyContextExport->ReleaseVertexInterface( pVertexExport );    
  2214.  
  2215.             if( iVertexType == RIGID_TYPE )
  2216.             {
  2217.                 pRigidVertexExport = (IPhyRigidVertex *)pPhyContextExport->GetVertexInterface(iVertex);
  2218.                 if (pRigidVertexExport == NULL)
  2219.                 {
  2220.                     OutputDebugString("Couldn't get rigid vertex export interface!");
  2221.                     hr = E_FAIL;
  2222.                     goto e_Exit;
  2223.                 }
  2224.                 // Get the vertex info!
  2225.             
  2226.                 pBoneNode = pRigidVertexExport->GetNode();
  2227.  
  2228.                 pbsCurData = FindBoneData(pBoneNode, rgbsBoneData, psmSkinMap->m_cbiBones);
  2229.                 if (pbsCurData == NULL)
  2230.                 {
  2231.                     assert(0);
  2232.                     OutputDebugString("AddSkinData: Bone node not found on second pass\n");
  2233.                     hr = E_FAIL;
  2234.                     goto e_Exit;
  2235.                 }
  2236.  
  2237.                 pbsCurData->m_rgdwIndices[pbsCurData->m_iCurIndex] = iVertex;
  2238.                 pbsCurData->m_rgfWeights[pbsCurData->m_iCurIndex] = 1.0f;
  2239.                 pbsCurData->m_iCurIndex += 1;
  2240.  
  2241.                 pPhyContextExport->ReleaseVertexInterface( pRigidVertexExport);
  2242.             }
  2243.             else
  2244.             {
  2245.                 assert( iVertexType == RIGID_BLENDED_TYPE );
  2246.  
  2247.                 pBlendedRigidVertexExport = (IPhyBlendedRigidVertex *)pPhyContextExport->GetVertexInterface(iVertex);
  2248.                 if (pBlendedRigidVertexExport == NULL)
  2249.                 {
  2250.                     OutputDebugString("Couldn't get blended rigid vertex export interface!");
  2251.                     hr = E_FAIL;
  2252.                     goto e_Exit;
  2253.                 }
  2254.  
  2255.                 // How many nodes affect his vertex?
  2256.                 cTotalBones = pBlendedRigidVertexExport->GetNumberNodes();
  2257.                 for (iBone = 0; iBone < cTotalBones; iBone++ )
  2258.                 {
  2259.                     pBoneNode = pBlendedRigidVertexExport->GetNode(iBone);
  2260.  
  2261.                     pbsCurData = FindBoneData(pBoneNode, rgbsBoneData, psmSkinMap->m_cbiBones);
  2262.                     if (pbsCurData == NULL)
  2263.                     {
  2264.                         assert(0);
  2265.                         OutputDebugString("AddSkinData: Bone node not found on second pass\n");
  2266.                         hr = E_FAIL;
  2267.                         goto e_Exit;
  2268.                     }
  2269.  
  2270.                     fWeight = pBlendedRigidVertexExport->GetWeight(iBone);
  2271.  
  2272.                     // first see if it is a repeat weight, is so just add to previous
  2273.                     if ((pbsCurData->m_iCurIndex > 0) && (pbsCurData->m_rgdwIndices[pbsCurData->m_iCurIndex-1] == iVertex))
  2274.                     {
  2275.                         pbsCurData->m_rgfWeights[pbsCurData->m_iCurIndex-1] += fWeight;
  2276.                     }
  2277.                     else
  2278.                     {
  2279.                         pbsCurData->m_rgdwIndices[pbsCurData->m_iCurIndex] = iVertex;
  2280.                         pbsCurData->m_rgfWeights[pbsCurData->m_iCurIndex] = fWeight;
  2281.                         pbsCurData->m_iCurIndex += 1;
  2282.                     }                    
  2283.                 }
  2284.  
  2285.                 pPhyContextExport->ReleaseVertexInterface( pBlendedRigidVertexExport);
  2286.             }
  2287.         }
  2288.     }
  2289. e_NoPhysique:
  2290.  
  2291.  
  2292.  
  2293.     if (rgbsBoneData != NULL)
  2294.     {
  2295.         assert(psmSkinMap != NULL);
  2296.  
  2297. // now deal with the wonderful world of duplicated indices
  2298.         rgbsBoneDataDups = new SBoneState[psmSkinMap->m_cbiBones];
  2299.         if (rgbsBoneDataDups == NULL)
  2300.         {
  2301.             hr = E_OUTOFMEMORY;
  2302.             goto e_Exit;
  2303.         }
  2304.  
  2305.         // first calculate new lengths for the bone weight arrays
  2306.         for (iBone = 0; iBone < psmSkinMap->m_cbiBones; iBone++)
  2307.         {
  2308.             pbsOldData = &rgbsBoneData[iBone];
  2309.             pbsCurData = &rgbsBoneDataDups[iBone];
  2310.  
  2311.             pbsCurData->m_cVertices = pbsOldData->m_cVertices;
  2312.             for (iIndex = 0; iIndex < pbsOldData->m_cVertices; iIndex++)
  2313.             {
  2314.                 iVertex = pbsOldData->m_rgdwIndices[iIndex];
  2315.  
  2316.                 iNextWedge = pMeshData->m_rgVertices[iVertex].iWedgeList;
  2317.                 while (iVertex != iNextWedge)
  2318.                 {
  2319.                     pbsCurData->m_cVertices += 1;
  2320.  
  2321.                     iNextWedge = pMeshData->m_rgVertices[iNextWedge].iWedgeList;
  2322.                 }
  2323.             }
  2324.         }
  2325.  
  2326.         // next build 
  2327.         for (iBone = 0; iBone < psmSkinMap->m_cbiBones; iBone++)
  2328.         {
  2329.             pbsOldData = &rgbsBoneData[iBone];
  2330.             pbsCurData = &rgbsBoneDataDups[iBone];
  2331.  
  2332.             pbsCurData->m_pBoneNode = pbsOldData->m_pBoneNode;
  2333.             pbsCurData->m_iCurIndex = 0;
  2334.  
  2335.             // allocate memory to pass to D3DXOF lib
  2336.             pbsCurData->m_cbData = sizeof(TCHAR*)
  2337.                                     + sizeof(DWORD) // numWeights
  2338.                                     + sizeof(DWORD) * pbsCurData->m_cVertices // array of vertex indices
  2339.                                     + sizeof(float) * pbsCurData->m_cVertices // array of weights
  2340.                                     + sizeof(float) * 16; // offset matrix
  2341.  
  2342.             pbCur = pbsCurData->m_pbData = new BYTE[pbsCurData->m_cbData];
  2343.             pbsCurData->m_szBoneName = psc->m_stStrings.CreateNiceString(pbsCurData->m_pBoneNode->GetName());
  2344.             if ((pbsCurData->m_pbData == NULL) || (pbsCurData->m_szBoneName == NULL))
  2345.             {
  2346.                 hr = E_OUTOFMEMORY;
  2347.                 goto e_Exit;
  2348.             }
  2349.  
  2350.             // fill first few entries, and remember pointers to the other arrays/matrix
  2351.             WRITE_PTCHAR(pbCur, pbsCurData->m_szBoneName);
  2352.             WRITE_DWORD(pbCur, pbsCurData->m_cVertices);
  2353.  
  2354.             pbsCurData->m_rgdwIndices = (DWORD*)pbCur;
  2355.             pbCur += sizeof(DWORD) * pbsCurData->m_cVertices;
  2356.  
  2357.             pbsCurData->m_rgfWeights = (float*)pbCur;
  2358.             pbCur += sizeof(float) * pbsCurData->m_cVertices;
  2359.  
  2360.             pbsCurData->m_pmatOffset = (D3DXMATRIX*)pbCur;
  2361.  
  2362.             // already loaded above, copy from the original state data
  2363.             *pbsCurData->m_pmatOffset = *pbsOldData->m_pmatOffset;
  2364.  
  2365.  
  2366.             // now that we have the new states set up, copy the data from the old states
  2367.             for (iIndex = 0; iIndex < pbsOldData->m_cVertices; iIndex++)
  2368.             {
  2369.                 iVertex = pbsOldData->m_rgdwIndices[iIndex];
  2370.  
  2371.                 pbsCurData->m_rgdwIndices[pbsCurData->m_iCurIndex] = iVertex;
  2372.                 pbsCurData->m_rgfWeights[pbsCurData->m_iCurIndex] = pbsOldData->m_rgfWeights[iIndex];
  2373.                 pbsCurData->m_iCurIndex += 1;
  2374.  
  2375.                 iNextWedge = pMeshData->m_rgVertices[iVertex].iWedgeList;
  2376.                 while (iVertex != iNextWedge)
  2377.                 {
  2378.                     pbsCurData->m_rgdwIndices[pbsCurData->m_iCurIndex] = iNextWedge;
  2379.                     pbsCurData->m_rgfWeights[pbsCurData->m_iCurIndex] = pbsOldData->m_rgfWeights[iIndex];
  2380.                     pbsCurData->m_iCurIndex += 1;
  2381.  
  2382.                     iNextWedge = pMeshData->m_rgVertices[iNextWedge].iWedgeList;
  2383.                 }
  2384.             }
  2385.         }
  2386.  
  2387.         // now that we do have skin data to put in the file, add a skinning header record
  2388.         cbHeaderData = sizeof(DWORD) * 3;
  2389.         pbCur = pbHeaderData = new BYTE[cbHeaderData];
  2390.         if (pbHeaderData == NULL)
  2391.         {
  2392.             hr = E_OUTOFMEMORY;
  2393.             goto e_Exit;
  2394.         }
  2395.  
  2396.         WRITE_WORD(pbCur, psc->m_cMaxWeightsPerVertex);
  2397.         WRITE_WORD(pbCur, psc->m_cMaxWeightsPerFace);
  2398.         WRITE_WORD(pbCur, psmSkinMap->m_cbiBones);
  2399.  
  2400.         hr = psc->m_pxofsave->CreateDataObject(DXFILEOBJ_XSkinMeshHeader,
  2401.                                         NULL,
  2402.                                         NULL,
  2403.                                         cbHeaderData,
  2404.                                         pbHeaderData,
  2405.                                         &pDataObject
  2406.                                         );
  2407.         hr = pParent->AddDataObject(pDataObject);
  2408.         if (FAILED(hr))
  2409.             goto e_Exit;
  2410.  
  2411.         RELEASE(pDataObject);
  2412.  
  2413. // now actually output the prepared buffers
  2414.         for (iBone = 0; iBone < psmSkinMap->m_cbiBones; iBone++)
  2415.         {
  2416.             assert(rgbsBoneData[iBone].m_iCurIndex == rgbsBoneData[iBone].m_cVertices);
  2417.             assert(rgbsBoneDataDups[iBone].m_iCurIndex == rgbsBoneDataDups[iBone].m_cVertices);
  2418.  
  2419.             hr = psc->m_pxofsave->CreateDataObject(DXFILEOBJ_SkinWeights,
  2420.                                             NULL,
  2421.                                             NULL,
  2422.                                             rgbsBoneDataDups[iBone].m_cbData,
  2423.                                             rgbsBoneDataDups[iBone].m_pbData,
  2424.                                             &pDataObject
  2425.                                             );
  2426.             if (FAILED(hr))
  2427.                 goto e_Exit;
  2428.  
  2429.             hr = pParent->AddDataObject(pDataObject);
  2430.             if (FAILED(hr))
  2431.                 goto e_Exit;
  2432.  
  2433.             RELEASE(pDataObject);
  2434.         }
  2435.     }
  2436.  
  2437. e_Exit:
  2438.     delete []pbHeaderData;
  2439.     delete []rgbsBoneData;
  2440.     delete []rgbsBoneDataDups;
  2441.     return hr;
  2442. }
  2443.  
  2444. HRESULT AddMesh
  2445.     (
  2446.     SSaveContext *psc,
  2447.     INode *pNode, 
  2448.     Object* pObj,
  2449.     LPDIRECTXFILEDATA pParent
  2450.     )
  2451. {
  2452.     HRESULT hr = S_OK;
  2453.     BOOL bDeleteTriObject = false;
  2454.     TriObject *pTriObject = NULL;
  2455.     Mesh *pMesh;
  2456.     BOOL bSwapTriOrder;
  2457.     PBYTE pbData = NULL;
  2458.     PBYTE pbCur;
  2459.     DWORD cbSize;
  2460.     DWORD cVertices;
  2461.     DWORD cFaces;
  2462.     DWORD iFace;
  2463.     Matrix3 matNodeTM;
  2464.     SMeshData MeshData;
  2465.     LPDIRECTXFILEDATA pDataObject = NULL;
  2466.     DWORD iVertex;
  2467.     DWORD *rgdwMeshMaterials = NULL;
  2468.     SCropInfo *rgCropInfo = NULL;
  2469.     D3DXMATERIAL *rgMaterials = NULL;
  2470.     DWORD cMaterials;
  2471.  
  2472.     // Retrieve the TriObject from the node
  2473.  
  2474.     pTriObject = GetTriObjectFromObjRef(pObj, &bDeleteTriObject);
  2475.  
  2476.     // If no TriObject then skip
  2477.     if (pTriObject == NULL) 
  2478.         goto e_Exit;
  2479.  
  2480.     pMesh = &(pTriObject->mesh);
  2481.     pMesh->checkNormals(TRUE); // TODO: Is this necessary?
  2482.     matNodeTM = pNode->GetNodeTM(psc->m_iTime);
  2483.     bSwapTriOrder = matNodeTM.Parity();
  2484.  
  2485.     hr = GatherMeshMaterials(psc, pNode, pMesh, &rgdwMeshMaterials, &rgMaterials, &rgCropInfo, &cMaterials);
  2486.     if (FAILED(hr))
  2487.         goto e_Exit;
  2488.  
  2489.     hr = GenerateMeshData(pMesh, &MeshData, rgdwMeshMaterials);
  2490.     if (FAILED(hr))
  2491.         goto e_Exit;
  2492.  
  2493.     cVertices = MeshData.m_cVertices;
  2494.     cFaces = MeshData.m_cFaces;
  2495.     cbSize = sizeof(DWORD) // nVertices
  2496.              + cVertices*sizeof(float)*3 // vertices
  2497.              + sizeof(DWORD) // nFaces
  2498.              + cFaces*(sizeof(DWORD) /*nFaceVertexIndices*/ 
  2499.                             + sizeof(DWORD)*3 /*faceVertexIndices*/); // faces
  2500.  
  2501.     pbCur = pbData = new BYTE[cbSize];
  2502.     if (pbData == NULL)
  2503.     {
  2504.         hr = E_OUTOFMEMORY;
  2505.         goto e_Exit;
  2506.     }
  2507.  
  2508.     // write nVertices
  2509.     WRITE_DWORD(pbCur, cVertices);
  2510.  
  2511.     // write vertices
  2512.     for (iVertex = 0; iVertex < MeshData.m_cVertices; iVertex++)
  2513.     {
  2514.         WRITE_POINT3(pbCur, pMesh->verts[MeshData.m_rgVertices[iVertex].iPointRep]);
  2515.     }
  2516.  
  2517.     // write nFaces
  2518.     WRITE_DWORD(pbCur, cFaces);
  2519.     
  2520.     // write faces
  2521.     for( iFace = 0; iFace < cFaces; iFace++ )
  2522.     {
  2523.         WRITE_DWORD(pbCur, 3); //nFaceVertexIndices
  2524.  
  2525.         // face vertex indices
  2526.         if( bSwapTriOrder )
  2527.         {
  2528.             WRITE_DWORD(pbCur, MeshData.m_rgFaces[iFace].index[0]);
  2529.             WRITE_DWORD(pbCur, MeshData.m_rgFaces[iFace].index[2]);
  2530.             WRITE_DWORD(pbCur, MeshData.m_rgFaces[iFace].index[1]);
  2531.         }
  2532.         else
  2533.         {
  2534.             WRITE_DWORD(pbCur, MeshData.m_rgFaces[iFace].index[0]);
  2535.             WRITE_DWORD(pbCur, MeshData.m_rgFaces[iFace].index[1]);
  2536.             WRITE_DWORD(pbCur, MeshData.m_rgFaces[iFace].index[2]);
  2537.         }
  2538.     }
  2539.  
  2540.     hr = psc->m_pxofsave->CreateDataObject(TID_D3DRMMesh,
  2541.                                     NULL,
  2542.                                     NULL,
  2543.                                     cbSize,
  2544.                                     pbData,
  2545.                                     &pDataObject
  2546.                                     );
  2547.     if (FAILED(hr))
  2548.         goto e_Exit;
  2549.  
  2550.     // add the normals to the file
  2551.     hr = AddNormals(psc, &MeshData, bSwapTriOrder, pDataObject);
  2552.     if (FAILED(hr))
  2553.         goto e_Exit;
  2554.  
  2555.     // write texture coordinates
  2556.     hr = AddTextureCoordinates(psc, &MeshData, rgCropInfo, pMesh, pDataObject);
  2557.     if (FAILED(hr))
  2558.         goto e_Exit;
  2559.  
  2560.     hr = AddVertexDuplicationIndices(psc, &MeshData, pDataObject);
  2561.     if (FAILED(hr))
  2562.         goto e_Exit;
  2563.  
  2564.     hr = AddMeshMaterials(psc, pNode, pMesh, rgdwMeshMaterials, rgMaterials, cMaterials, pDataObject);
  2565.     if (FAILED(hr))
  2566.         goto e_Exit;
  2567.  
  2568.     hr = AddSkinData(psc, pNode, &MeshData, pDataObject);
  2569.     if (FAILED(hr))
  2570.         goto e_Exit;
  2571.  
  2572.     hr = pParent->AddDataObject(pDataObject);
  2573.     if (FAILED(hr))
  2574.         goto e_Exit;
  2575.  
  2576. e_Exit:
  2577.     if (bDeleteTriObject)
  2578.     {
  2579.         delete pTriObject;
  2580.     }
  2581.  
  2582.     RELEASE(pDataObject);
  2583.     delete []rgdwMeshMaterials;
  2584.     delete []rgMaterials;
  2585.     delete []rgCropInfo;
  2586.     return hr;
  2587. }
  2588.  
  2589. HRESULT
  2590. AddPatchMeshMaterials(
  2591.     SSaveContext *psc,
  2592.     INode *pNode,
  2593.     PatchMesh *pPatchMesh,
  2594.     LPDIRECTXFILEDATA pParent
  2595.     )
  2596. {
  2597.     HRESULT hr = S_OK;
  2598.     LPDIRECTXFILEDATA pDataObject = NULL;
  2599.     Mtl *pMtlMain;
  2600.     Mtl *pMtlCur;
  2601.     DWORD cSubMaterials;
  2602.     DWORD iPatch;
  2603.     DWORD cPatches;
  2604.     BOOL bNoSubMaterials;
  2605.     BOOL bWireframeColor;
  2606.     PBYTE pbCur;
  2607.     PBYTE pbData = NULL;
  2608.     DWORD cbSize;
  2609.     DWORD iCurMatID;
  2610.     DWORD iCurMaterial;
  2611.  
  2612.     pMtlMain = pNode->GetMtl();
  2613.     cPatches = pPatchMesh->getNumPatches();
  2614.     cSubMaterials = 0;
  2615.     bNoSubMaterials = FALSE;
  2616.     bWireframeColor = FALSE;
  2617.  
  2618.     // The color of a given mesh can be provided by three different sources:
  2619.     //   1) applied texture maps, as part of a material
  2620.     //   2) explicitly defined & applied materials without texture maps
  2621.     //   3) a 'wireframe' color.
  2622.     
  2623.     // For our purposes, we will output these in this order of preference, ignoring
  2624.     // processing past the first one we find.
  2625.  
  2626.     cbSize = sizeof(DWORD) // nMaterials
  2627.                 + sizeof(DWORD) // nFaceIndexes
  2628.                 + cPatches*sizeof(DWORD); // face indexes
  2629.  
  2630.     pbData = pbCur = new BYTE[cbSize];
  2631.     if (pbCur == NULL)
  2632.     {
  2633.         hr = E_OUTOFMEMORY;
  2634.         goto e_Exit;
  2635.     }
  2636.     
  2637.     if (pMtlMain != NULL)
  2638.     {
  2639.         // There is at least one material. We're in case 1) or 2)
  2640.  
  2641.         cSubMaterials = pMtlMain->NumSubMtls();
  2642.         if (cSubMaterials < 1)
  2643.         {
  2644.             // Count the material itself as a submaterial.
  2645.             cSubMaterials = 1;
  2646.             bNoSubMaterials = TRUE;
  2647.         }
  2648.     }
  2649.     else  // no material, then we'll create a material corresponding to the default wire color.
  2650.     {
  2651.         bWireframeColor = TRUE;
  2652.         cSubMaterials = 1;
  2653.     }
  2654.  
  2655.     WRITE_DWORD(pbCur, cSubMaterials);
  2656.     WRITE_DWORD(pbCur, cPatches);
  2657.  
  2658.  
  2659.     // For each face, output the index of the material which applies to it, 
  2660.     // starting from  0
  2661.  
  2662.     for (iPatch=0; iPatch < cPatches; iPatch++)
  2663.     {
  2664.  
  2665.         if (bWireframeColor || bNoSubMaterials) 
  2666.         {
  2667.             // If we're using wireframe color, it's our only material
  2668.             WRITE_DWORD(pbCur, 0);
  2669.         }
  2670.         else
  2671.         {
  2672.             // Otherwise we have at least one material to process.
  2673.  
  2674.             iCurMatID = pPatchMesh->getPatchMtlIndex(iPatch);
  2675.  
  2676.             if (cSubMaterials == 1)
  2677.             {
  2678.                 iCurMatID = 0;
  2679.             }
  2680.             else
  2681.             {
  2682.                 // SDK recommends mod'ing the material ID by the valid # of materials, 
  2683.                 // as sometimes a material number that's too high is returned.
  2684.                 iCurMatID %= cSubMaterials;
  2685.             }
  2686.  
  2687.             // output the appropriate material color
  2688.  
  2689.             WRITE_DWORD(pbCur, iCurMatID);
  2690.  
  2691.         } 
  2692.  
  2693.     } 
  2694.  
  2695.     // now finally create the mesh material list
  2696.     hr = psc->m_pxofsave->CreateDataObject(TID_D3DRMMeshMaterialList,
  2697.                                     NULL,
  2698.                                     NULL,
  2699.                                     cbSize,
  2700.                                     pbData,
  2701.                                     &pDataObject
  2702.                                     );
  2703.     if (FAILED(hr))
  2704.         goto e_Exit;
  2705.  
  2706.     hr = pParent->AddDataObject(pDataObject);
  2707.     if (FAILED(hr))
  2708.         goto e_Exit;
  2709.  
  2710.     // 3DSMax uses wireframe color as its default material for a mesh.
  2711.     // Output the wireframe color as the material if there are no explicit materials.
  2712.  
  2713.     if (bWireframeColor)
  2714.     {
  2715.         AddWireframeMaterial(psc, pNode, pDataObject);
  2716.     } 
  2717.     else
  2718.     {
  2719.         // 3DSMax allows multiple materials to be used on a single mesh via
  2720.         // 'submaterials'. When the first submaterial is defined, the main material
  2721.         // is copied into submaterial #1, and the new submaterial because submaterial #2.
  2722.         // 
  2723.         // We have to catch the case where there's a material, but no submaterials. For this
  2724.         // case, set NumSubMaterials to 1 which would never happen otherwise. It's imperative
  2725.         // that the first material be set to MtlMain, rather than trying to GetSubMtl() to 
  2726.         // allow this logic to work.
  2727.  
  2728.         // Loop through the materials (if any) and output them.
  2729.  
  2730.         for (iCurMaterial = 0; (iCurMaterial < cSubMaterials); iCurMaterial++)
  2731.         {
  2732.             if (bNoSubMaterials)
  2733.             {
  2734.                 // We're on the parent material, possibly the ONLY material.
  2735.                 // We won't be able to get it with GetSubMtl() if it's the only one, and
  2736.                 // the data in the first submaterial is identical to the main material,
  2737.                 // so just use the main material.
  2738.  
  2739.                 pMtlCur = pMtlMain;
  2740.             }
  2741.             else
  2742.             {
  2743.                 // We're into the true submaterials. Safe to get with 'GetSubMtl'
  2744.  
  2745.                 pMtlCur = pMtlMain->GetSubMtl(iCurMaterial);
  2746.             }
  2747.  
  2748.             hr = AddMaterial(psc, pMtlCur, pDataObject);
  2749.             if (FAILED(hr))
  2750.                 goto e_Exit;
  2751.         } 
  2752.     }
  2753.  
  2754. e_Exit:
  2755.     delete []pbData;
  2756.     RELEASE(pDataObject);
  2757.  
  2758.  
  2759.     return hr;
  2760.  
  2761. HRESULT AddPatchMesh
  2762.     (
  2763.     SSaveContext *psc,
  2764.     INode *pNode, 
  2765.     Object* pObj,
  2766.     LPDIRECTXFILEDATA pParent
  2767.     )
  2768. {
  2769.     HRESULT    hr = S_OK;
  2770.     LPDIRECTXFILEDATA pDataObject = NULL;
  2771.     PBYTE         pbData = NULL;
  2772.     PBYTE          pbCur = NULL;        
  2773.     DWORD          cbSize;
  2774.     DWORD iVertex;
  2775.     DWORD iPatch;
  2776.     Point3 *pvVertices;
  2777.     PatchMesh *pPatchMesh;
  2778.     PatchObject *pPatchObject = NULL;
  2779.     BOOL bDeletePatchObject;
  2780.     DWORD iControl;
  2781.     DWORD cPatchIndices;
  2782.     SPatchMeshData PatchMeshData;
  2783.  
  2784.     assert(psc != NULL);
  2785.     assert(pNode != NULL);
  2786.     assert(pObj != NULL);
  2787.     assert(pParent != NULL);
  2788.  
  2789.     pPatchObject = GetPatchObjectFromObjRef(pObj, &bDeletePatchObject);
  2790.  
  2791.     // If no PatchObject then skip
  2792.     if (pPatchObject == NULL) 
  2793.         goto e_Exit;
  2794.  
  2795.     pPatchMesh = &pPatchObject->patch;
  2796.  
  2797.     // massage the patch data into an outputable format (including texture coord handling)
  2798.     hr = GeneratePatchMeshData(pPatchMesh, &PatchMeshData);
  2799.     if (FAILED(hr))
  2800.         goto e_Exit;
  2801.  
  2802.     // figure out the total number of control indices
  2803.     cPatchIndices = 0;
  2804.     for (iPatch = 0; iPatch < PatchMeshData.m_cPatches; iPatch++)
  2805.     {
  2806.         cPatchIndices += PatchMeshData.m_rgPatches[iPatch].m_cControl;
  2807.     }
  2808.  
  2809.     cbSize = sizeof(DWORD) // nVertices
  2810.              + PatchMeshData.m_cVertices * sizeof(float)*3 // vertices
  2811.              + sizeof(DWORD) // nPatches
  2812.              + PatchMeshData.m_cPatches * sizeof(DWORD) /*nControlIndices*/ 
  2813.              + cPatchIndices * sizeof(DWORD) /*controlIndices*/; // patches
  2814.  
  2815.     pbCur = pbData = new BYTE[cbSize];
  2816.     if (pbData == NULL)
  2817.     {
  2818.         hr = E_OUTOFMEMORY;
  2819.         goto e_Exit;
  2820.     }
  2821.  
  2822.     // write the number of vertices
  2823.     WRITE_DWORD(pbCur, PatchMeshData.m_cVertices);
  2824.  
  2825.     // first write all the vertices
  2826.     pvVertices = (Point3*)pbCur;
  2827.     for (iVertex = 0; iVertex < PatchMeshData.m_cVertices; iVertex++)
  2828.     {
  2829.         pvVertices[iVertex] = PatchMeshData.m_rgVertices[iVertex].vPosition;
  2830.     }
  2831.  
  2832.     // skip over the vertices
  2833.     pbCur = pbData + sizeof(DWORD) + sizeof(float)*3*PatchMeshData.m_cVertices;
  2834.  
  2835.     // write the number of patches
  2836.     WRITE_DWORD(pbCur, PatchMeshData.m_cPatches);
  2837.  
  2838.     // now write all the patch data
  2839.     for (iPatch = 0; iPatch < PatchMeshData.m_cPatches; iPatch++)
  2840.     {
  2841.         WRITE_DWORD(pbCur, PatchMeshData.m_rgPatches[iPatch].m_cControl);
  2842.         for (iControl = 0; iControl < PatchMeshData.m_rgPatches[iPatch].m_cControl; iControl++)
  2843.         {
  2844.             WRITE_DWORD(pbCur, PatchMeshData.m_rgPatches[iPatch].m_rgdwControl[iControl]);
  2845.         }
  2846.     }
  2847.  
  2848.     hr = psc->m_pxofsave->CreateDataObject(DXFILEOBJ_PatchMesh,
  2849.                                     NULL,
  2850.                                     NULL,
  2851.                                     cbSize,
  2852.                                     pbData,
  2853.                                     &pDataObject
  2854.                                     );
  2855.     if (FAILED(hr))
  2856.         goto e_Exit;
  2857.  
  2858.     hr = AddPatchTextureCoordinates(psc, &PatchMeshData, pPatchMesh, pDataObject);
  2859.     if (FAILED(hr))
  2860.         goto e_Exit;
  2861.  
  2862.     hr = AddPatchVertexDuplicationIndices(psc, &PatchMeshData, pDataObject);
  2863.     if (FAILED(hr))
  2864.         goto e_Exit;
  2865.  
  2866.     hr = AddPatchMeshMaterials(psc, pNode, pPatchMesh, pDataObject);
  2867.     if (FAILED(hr))
  2868.         goto e_Exit;
  2869.  
  2870.     hr = pParent->AddDataObject(pDataObject);
  2871.     if (FAILED(hr))
  2872.         goto e_Exit;
  2873.  
  2874. e_Exit:
  2875.     delete []pbData;
  2876.     RELEASE(pDataObject);
  2877.  
  2878.     if (bDeletePatchObject)
  2879.     {
  2880.         delete pPatchObject;
  2881.     }
  2882.  
  2883.     return hr;
  2884. }
  2885.  
  2886. HRESULT AddTransform
  2887.     (
  2888.     SSaveContext *psc,
  2889.     INode *pNode, 
  2890.     LPDIRECTXFILEDATA pParent
  2891.     )
  2892. {
  2893.     HRESULT hr = S_OK;
  2894.     PBYTE pbData = NULL;
  2895.     PBYTE pbCur;
  2896.     DWORD cbSize;
  2897.     Matrix3 matNodeTM;
  2898.     Matrix3 matRelativeTM;
  2899.     LPDIRECTXFILEDATA pDataObject = NULL;
  2900.  
  2901.     cbSize = 16*sizeof(float);
  2902.  
  2903.     pbCur = pbData = new BYTE[cbSize];
  2904.     if (pbData == NULL)
  2905.     {
  2906.         hr = E_OUTOFMEMORY;
  2907.         goto e_Exit;
  2908.     }
  2909.  
  2910.     // Node transform
  2911.     matNodeTM = pNode->GetNodeTM(psc->m_iTime);
  2912.     if( pNode->IsRootNode() )
  2913.     {
  2914.         // scene root
  2915.         matRelativeTM = matNodeTM;
  2916.     }
  2917.     else
  2918.     {
  2919.         Matrix3 matParentTM = pNode->GetParentTM(psc->m_iTime);
  2920.         if( memcmp(&matNodeTM,&matParentTM,12*sizeof(float)) == 0 )
  2921.         {
  2922.             matRelativeTM.IdentityMatrix();
  2923.         }
  2924.         else
  2925.         {
  2926.             matRelativeTM = matNodeTM * Inverse(matParentTM);
  2927.         }
  2928.     }
  2929.  
  2930.     WRITE_MATRIX4_FROM_MATRIX3(pbCur, matRelativeTM);
  2931.  
  2932.     hr = psc->m_pxofsave->CreateDataObject(TID_D3DRMFrameTransformMatrix,
  2933.                                     NULL,
  2934.                                     NULL,
  2935.                                     cbSize,
  2936.                                     pbData,
  2937.                                     &pDataObject
  2938.                                     );
  2939.     if (FAILED(hr))
  2940.         goto e_Exit;
  2941.  
  2942.     hr = pParent->AddDataObject(pDataObject);
  2943.     if (FAILED(hr))
  2944.         goto e_Exit;
  2945.  
  2946. e_Exit:
  2947.     delete []pbData;
  2948.     RELEASE(pDataObject);
  2949.  
  2950.     return hr;
  2951. }
  2952.  
  2953. HRESULT AddObjectOffsetTransform
  2954.     (
  2955.     SSaveContext *psc,
  2956.     INode *pNode,
  2957.     LPDIRECTXFILEDATA pParent,
  2958.     LPDIRECTXFILEDATA *ppMeshParent
  2959.     )
  2960. {
  2961.     HRESULT hr = S_OK;
  2962.     PBYTE pbData = NULL;
  2963.     PBYTE pbCur;
  2964.     DWORD cbSize;
  2965.     LPDIRECTXFILEDATA pDataObject = NULL;
  2966.     Matrix3 matNodeTM;
  2967.     Matrix3 matObjTMAfterWSM;
  2968.     Matrix3 matObjectOffset;
  2969.     LPDIRECTXFILEDATA pObjectOffset = NULL;
  2970.  
  2971.     // check to see if the node has an object offset matrix
  2972.     matNodeTM = pNode->GetNodeTM(psc->m_iTime);
  2973.     matObjTMAfterWSM = pNode->GetObjTMAfterWSM(psc->m_iTime);
  2974.     if (memcmp(&matObjTMAfterWSM, &matNodeTM,12*sizeof(Matrix3)) != 0)
  2975.     {
  2976.         // the mesh is positioned offset from the node, so add another
  2977.         //   frame (unnamed) to offset the mesh without affecting the node's children
  2978.         //   and/or animation attached to the node
  2979.         hr = psc->m_pxofsave->CreateDataObject(TID_D3DRMFrame,
  2980.                                         NULL,
  2981.                                         NULL,
  2982.                                         0,
  2983.                                         NULL,
  2984.                                         &pObjectOffset
  2985.                                         );
  2986.  
  2987.         matObjectOffset = matObjTMAfterWSM * Inverse(matNodeTM);
  2988.  
  2989.  
  2990.         cbSize = 16*sizeof(float);
  2991.  
  2992.         pbCur = pbData = new BYTE[cbSize];
  2993.         if (pbData == NULL)
  2994.         {
  2995.             hr = E_OUTOFMEMORY;
  2996.             goto e_Exit;
  2997.         }
  2998.  
  2999.         WRITE_MATRIX4_FROM_MATRIX3(pbCur, matObjectOffset);
  3000.  
  3001.         hr = psc->m_pxofsave->CreateDataObject(TID_D3DRMFrameTransformMatrix,
  3002.                                         NULL,
  3003.                                         NULL,
  3004.                                         cbSize,
  3005.                                         pbData,
  3006.                                         &pDataObject
  3007.                                         );
  3008.         if (FAILED(hr))
  3009.             goto e_Exit;
  3010.  
  3011.         hr = pObjectOffset->AddDataObject(pDataObject);
  3012.         if (FAILED(hr))
  3013.             goto e_Exit;
  3014.  
  3015.         hr = pParent->AddDataObject(pObjectOffset);
  3016.         if (FAILED(hr))
  3017.             goto e_Exit;
  3018.  
  3019.  
  3020.         *ppMeshParent = pObjectOffset;
  3021.     }
  3022.     else  // identity object offset, mesh should use node as parent
  3023.     {
  3024.         *ppMeshParent = pParent;
  3025.     }
  3026.  
  3027. e_Exit:
  3028.     delete []pbData;
  3029.     RELEASE(pDataObject);
  3030.     RELEASE(pObjectOffset);
  3031.  
  3032.     return hr;
  3033. }
  3034.  
  3035. HRESULT EnumTree
  3036.     (
  3037.     SSaveContext *psc,
  3038.     INode *pNode, 
  3039.     LPDIRECTXFILEDATA *ppDataObjectNew
  3040.     )
  3041. {
  3042.     HRESULT hr = S_OK;
  3043.     DWORD cChildren;
  3044.     DWORD iChild;
  3045.     LPDIRECTXFILEDATA pChildDataObject;
  3046.     LPDIRECTXFILEDATA pDataObject = NULL;
  3047.     LPDIRECTXFILEDATA pMeshParent;
  3048.     Object* pObj;
  3049.     char *szName = NULL;
  3050.     
  3051.     szName = psc->m_stStrings.CreateNiceString(pNode->GetName());
  3052.     if (szName == NULL)
  3053.     {
  3054.         hr = E_OUTOFMEMORY;
  3055.         goto e_Exit;
  3056.     }
  3057.  
  3058.     OutputDebugString(szName);
  3059.     OutputDebugString("\n");
  3060.  
  3061.     // add the node to the array for anim purposes
  3062.     assert( psc->m_cNodesCur < psc->m_cNodes );
  3063.     psc->m_rgpnNodes[psc->m_cNodesCur] = pNode;
  3064.     psc->m_cNodesCur += 1;
  3065.  
  3066.     hr = psc->m_pxofsave->CreateDataObject(TID_D3DRMFrame,
  3067.                                     szName,
  3068.                                     NULL,
  3069.                                     0,
  3070.                                     NULL,
  3071.                                     &pDataObject
  3072.                                     );
  3073.  
  3074.     hr = AddTransform(psc, pNode, pDataObject);
  3075.     if (FAILED(hr))
  3076.         goto e_Exit;
  3077.  
  3078.     if (psc->m_bSavePatchData && IsExportablePatchMesh(pNode, pObj) && !(psc->m_bSaveSelection && !pNode->Selected()))
  3079.     {
  3080.         hr = AddObjectOffsetTransform(psc, pNode, pDataObject, &pMeshParent);
  3081.         if (FAILED(hr))
  3082.             goto e_Exit;
  3083.  
  3084.         hr = AddPatchMesh(psc, pNode, pObj, pMeshParent);
  3085.         if (FAILED(hr))
  3086.             goto e_Exit;
  3087.     }
  3088.     else if (IsExportableMesh(pNode, pObj) && !(psc->m_bSaveSelection && !pNode->Selected()))
  3089.     {
  3090.         hr = AddObjectOffsetTransform(psc, pNode, pDataObject, &pMeshParent);
  3091.         if (FAILED(hr))
  3092.             goto e_Exit;
  3093.         
  3094.         hr = AddMesh(psc, pNode, pObj, pMeshParent);
  3095.         if (FAILED(hr))
  3096.             goto e_Exit;
  3097.     }
  3098.  
  3099.     cChildren = pNode->NumberOfChildren();
  3100.     for (iChild = 0; iChild < cChildren; iChild++)
  3101.     {
  3102.         // enumerate the child
  3103.         hr = EnumTree(psc, pNode->GetChildNode(iChild), &pChildDataObject);
  3104.         if (FAILED(hr))
  3105.             goto e_Exit;
  3106.         
  3107.         hr = pDataObject->AddDataObject(pChildDataObject);
  3108.         if (FAILED(hr))
  3109.             goto e_Exit;
  3110.  
  3111.         RELEASE(pChildDataObject);
  3112.     }
  3113.  
  3114.     *ppDataObjectNew = pDataObject;
  3115. e_Exit:
  3116.     return hr;
  3117. }
  3118.  
  3119.  
  3120. HRESULT Preprocess
  3121.     (
  3122.     SPreprocessContext *ppc,
  3123.     INode *pNode
  3124.     )
  3125. {
  3126.     HRESULT hr = S_OK;
  3127.     DWORD cChildren;
  3128.     DWORD iChild;
  3129.     Object* pObj;
  3130.     SSkinMap **rgpsmTemp = NULL;
  3131.     IPhyVertexExport *pVertexExport;
  3132.     IPhyRigidVertex* pRigidVertexExport;
  3133.     IPhyBlendedRigidVertex *pBlendedRigidVertexExport;
  3134.     INode* pBoneNode;
  3135.     SSkinMap *psmSkinMap;
  3136.     Modifier* pPhyMod = NULL;
  3137.     IPhysiqueExport* pPhyExport = NULL;
  3138.     IPhyContextExport* pPhyContextExport = NULL;
  3139.     DWORD iVertex;
  3140.     DWORD cVertices;
  3141.     DWORD iVertexType;
  3142.     SBoneInfo *pbi;
  3143.     DWORD cTotalBones;
  3144.     DWORD iBone;
  3145.     DWORD cpnBonesSeen;
  3146.     DWORD cpnBonesSeenMax;
  3147.     INode **rgpnBonesSeen = NULL;
  3148.     INode **rgpnTemp;
  3149.     BOOL bBoneSeen;
  3150.     DWORD iBoneSeen;
  3151.     BOOL bDeleteTriObject = false;
  3152.     TriObject *pTriObject = NULL;
  3153.     Mesh *pMesh;
  3154.     DWORD iFace;
  3155.     DWORD iPoint;
  3156.  
  3157.     ppc->m_cNodes += 1;
  3158.  
  3159.     if (IsExportableMesh(pNode,pObj) && !(ppc->m_bSaveSelection && !pNode->Selected()))
  3160.     {
  3161.         // first see if physique is present
  3162.         pPhyMod = FindPhysiqueModifier(pNode);
  3163.         if (pPhyMod != NULL)
  3164.         {
  3165.             // Get a Physique Export interface
  3166.             pPhyExport = (IPhysiqueExport *)pPhyMod->GetInterface(I_PHYINTERFACE);
  3167.             if (pPhyExport == NULL)
  3168.             {   // not all interfaces present, so ignore
  3169.                 goto e_NoPhysique;
  3170.             }
  3171.             // For a given Object's INode get a
  3172.             // ModContext Interface from the Physique Export Interface:
  3173.             pPhyContextExport = (IPhyContextExport *)pPhyExport->GetContextInterface(pNode);
  3174.             if(pPhyContextExport == NULL)
  3175.             {
  3176.                 // not all interfaces present, so ignore
  3177.                 goto e_NoPhysique;
  3178.             }
  3179.  
  3180.             // convert to rigid with blending
  3181.             pPhyContextExport->ConvertToRigid(TRUE);
  3182.             pPhyContextExport->AllowBlending(TRUE);
  3183.  
  3184.             psmSkinMap = new SSkinMap;
  3185.             if (psmSkinMap == NULL)
  3186.             {
  3187.                 hr = E_OUTOFMEMORY;
  3188.                 goto e_Exit;
  3189.             }
  3190.  
  3191.             // realloc if necessary
  3192.             if (ppc->m_cpsmSkinMaps == ppc->m_cpsmSkinMapsMax)
  3193.             {
  3194.                 rgpsmTemp = ppc->m_rgpsmSkinMaps;
  3195.  
  3196.                 ppc->m_cpsmSkinMapsMax = max(1, ppc->m_cpsmSkinMapsMax);
  3197.                 ppc->m_cpsmSkinMapsMax = ppc->m_cpsmSkinMapsMax * 2;
  3198.                 ppc->m_rgpsmSkinMaps = new SSkinMap*[ppc->m_cpsmSkinMapsMax];
  3199.                 if (ppc->m_rgpsmSkinMaps == NULL)
  3200.                 {
  3201.                     ppc->m_rgpsmSkinMaps = rgpsmTemp;
  3202.                     hr = E_OUTOFMEMORY;
  3203.                     goto e_Exit;
  3204.                 }
  3205.  
  3206.                 if (ppc->m_cpsmSkinMaps > 0)
  3207.                 {
  3208.                     memcpy(ppc->m_rgpsmSkinMaps, rgpsmTemp, sizeof(SSkinMap*) * ppc->m_cpsmSkinMaps);
  3209.                 }
  3210.  
  3211.                 delete []rgpsmTemp;
  3212.             }
  3213.             ppc->m_rgpsmSkinMaps[ppc->m_cpsmSkinMaps] = psmSkinMap;
  3214.             ppc->m_cpsmSkinMaps += 1;
  3215.  
  3216.             // init the map
  3217.             psmSkinMap->m_pMeshNode = pNode;
  3218.             psmSkinMap->m_cbiBonesMax = 30;
  3219.             psmSkinMap->m_rgbiBones = new SBoneInfo[psmSkinMap->m_cbiBonesMax];
  3220.             if (psmSkinMap->m_rgbiBones == NULL)
  3221.             {
  3222.                 hr = E_OUTOFMEMORY;
  3223.                 goto e_Exit;
  3224.             }
  3225.  
  3226.             // init duplication removal for redundant weights
  3227.             cpnBonesSeenMax = 30;
  3228.             cpnBonesSeen = 0;
  3229.             rgpnBonesSeen = new INode*[cpnBonesSeenMax];
  3230.             if (rgpnBonesSeen == NULL)
  3231.             {
  3232.                 hr = E_OUTOFMEMORY;
  3233.                 goto e_Exit;
  3234.             }
  3235.  
  3236.             cVertices = pPhyContextExport->GetNumberVertices();
  3237.             for (iVertex = 0; iVertex < cVertices; iVertex++ )
  3238.             {
  3239.                 pVertexExport = (IPhyVertexExport *)pPhyContextExport->GetVertexInterface(iVertex);    
  3240.                 if (pVertexExport == NULL)
  3241.                 {
  3242.                     OutputDebugString("Couldn't get export interface!");
  3243.                     hr = E_FAIL;
  3244.                     goto e_Exit;
  3245.                 }
  3246.             
  3247.                 // What kind of vertices are these?
  3248.                 iVertexType = pVertexExport->GetVertexType();
  3249.  
  3250.                 pPhyContextExport->ReleaseVertexInterface( pVertexExport );    
  3251.  
  3252.                 if( iVertexType == RIGID_TYPE )
  3253.                 {
  3254.                     pRigidVertexExport = (IPhyRigidVertex *)pPhyContextExport->GetVertexInterface(iVertex);
  3255.                     if (pRigidVertexExport == NULL)
  3256.                     {
  3257.                         OutputDebugString("Couldn't get rigid vertex export interface!");
  3258.                         hr = E_FAIL;
  3259.                         goto e_Exit;
  3260.                     }
  3261.                     // Get the vertex info!
  3262.                 
  3263.                     pBoneNode = pRigidVertexExport->GetNode();
  3264.  
  3265.                     pbi = psmSkinMap->FindBone(pBoneNode);
  3266.                     if (pbi == NULL)
  3267.                     {
  3268.                         hr = psmSkinMap->AddBone(pBoneNode, &pbi);
  3269.                         if (FAILED(hr))
  3270.                             goto e_Exit;
  3271.                     }
  3272.  
  3273.                     pbi->m_cVertices += 1;
  3274.  
  3275.                     ppc->m_cMaxWeightsPerVertex = max(1, ppc->m_cMaxWeightsPerVertex);
  3276.  
  3277.                     pPhyContextExport->ReleaseVertexInterface( pRigidVertexExport);
  3278.                 }
  3279.                 else
  3280.                 {
  3281.                     assert( iVertexType == RIGID_BLENDED_TYPE );
  3282.  
  3283.                     pBlendedRigidVertexExport = (IPhyBlendedRigidVertex *)pPhyContextExport->GetVertexInterface(iVertex);
  3284.                     if (pBlendedRigidVertexExport == NULL)
  3285.                     {
  3286.                         OutputDebugString("Couldn't get blended rigid vertex export interface!");
  3287.                         hr = E_FAIL;
  3288.                         goto e_Exit;
  3289.                     }
  3290.  
  3291.                     // How many nodes affect his vertex?
  3292.                     cTotalBones = pBlendedRigidVertexExport->GetNumberNodes();
  3293.                     cpnBonesSeen = 0;
  3294.                     for (iBone = 0; iBone < cTotalBones; iBone++ )
  3295.                     {
  3296.                         pBoneNode = pBlendedRigidVertexExport->GetNode(iBone);
  3297.  
  3298.                         // first see if the bone has already been seen
  3299.                         bBoneSeen = FALSE;
  3300.                         for (iBoneSeen = 0; iBoneSeen < cpnBonesSeen; iBoneSeen++)
  3301.                         {
  3302.                             if (rgpnBonesSeen[iBoneSeen] == pBoneNode)
  3303.                             {
  3304.                                 bBoneSeen = TRUE;
  3305.                                 break;
  3306.                             }
  3307.                         }
  3308.                         
  3309.                         // if not seen, collect stats and add to already seen
  3310.                         if (!bBoneSeen)
  3311.                         {
  3312.                             pbi = psmSkinMap->FindBone(pBoneNode);
  3313.                             if (pbi == NULL)
  3314.                             {
  3315.                                 hr = psmSkinMap->AddBone(pBoneNode, &pbi);
  3316.                                 if (FAILED(hr))
  3317.                                     goto e_Exit;
  3318.                             }
  3319.                             pbi->m_cVertices += 1;
  3320.  
  3321.                             if (cpnBonesSeen >= cpnBonesSeenMax)
  3322.                             {
  3323.                                 rgpnTemp = rgpnBonesSeen;
  3324.                                 cpnBonesSeenMax *= 2;
  3325.  
  3326.                                 rgpnBonesSeen = new INode*[cpnBonesSeenMax];
  3327.                                 if (rgpnBonesSeen == NULL)
  3328.                                 {
  3329.                                     rgpnBonesSeen = rgpnTemp;
  3330.                                     hr = E_OUTOFMEMORY;
  3331.                                     goto e_Exit;
  3332.                                 }
  3333.  
  3334.                                 memcpy(rgpnBonesSeen, rgpnTemp, cpnBonesSeen * sizeof(INode*));
  3335.                                 delete []rgpnTemp;
  3336.                             }
  3337.  
  3338.                             rgpnBonesSeen[cpnBonesSeen] = pBoneNode;
  3339.                             cpnBonesSeen += 1;
  3340.                         }
  3341.                     }
  3342.  
  3343.                     // actualNum... accounts for duplicated weights to same transform node
  3344.                     // that are combined automatically above
  3345.                     ppc->m_cMaxWeightsPerVertex = max(cpnBonesSeen, ppc->m_cMaxWeightsPerVertex);
  3346.             
  3347.                     pPhyContextExport->ReleaseVertexInterface( pBlendedRigidVertexExport);
  3348.                 }
  3349.             }
  3350.  
  3351.  
  3352.         // now figure out the max number of weights per face
  3353.  
  3354.             pTriObject = GetTriObjectFromObjRef(pObj, &bDeleteTriObject);
  3355.             if (pTriObject == NULL) 
  3356.             {
  3357.                 OutputDebugString("Physique info, but no mesh");
  3358.                 hr = E_FAIL;
  3359.                 goto e_Exit;
  3360.             }
  3361.  
  3362.             pMesh = &(pTriObject->mesh);
  3363.  
  3364.             for (iFace = 0; iFace < pMesh->numFaces; iFace++)
  3365.             {
  3366.                 cpnBonesSeen = 0;
  3367.                 for (iPoint = 0; iPoint < 3; iPoint++ )
  3368.                 {
  3369.                     iVertex = pMesh->faces[iFace].v[iPoint];
  3370.  
  3371.                     pVertexExport = (IPhyVertexExport *)pPhyContextExport->GetVertexInterface(iVertex);    
  3372.                     if (pVertexExport == NULL)
  3373.                     {
  3374.                         OutputDebugString("Couldn't get export interface!");
  3375.                         hr = E_FAIL;
  3376.                         goto e_Exit;
  3377.                     }
  3378.             
  3379.                     // What kind of vertices are these?
  3380.                     iVertexType = pVertexExport->GetVertexType();
  3381.  
  3382.                     pPhyContextExport->ReleaseVertexInterface( pVertexExport );    
  3383.  
  3384.                     if( iVertexType == RIGID_TYPE )
  3385.                     {
  3386.                         pRigidVertexExport = (IPhyRigidVertex *)pPhyContextExport->GetVertexInterface(iVertex);
  3387.                         if (pRigidVertexExport == NULL)
  3388.                         {
  3389.                             OutputDebugString("Couldn't get rigid vertex export interface!");
  3390.                             hr = E_FAIL;
  3391.                             goto e_Exit;
  3392.                         }
  3393.                         // Get the vertex info!
  3394.                 
  3395.                         pBoneNode = pRigidVertexExport->GetNode();
  3396.  
  3397.                         pbi = psmSkinMap->FindBone(pBoneNode);
  3398.                         if (pbi == NULL)
  3399.                         {
  3400.                             hr = psmSkinMap->AddBone(pBoneNode, &pbi);
  3401.                             if (FAILED(hr))
  3402.                                 goto e_Exit;
  3403.                         }
  3404.  
  3405.                         // first see if the bone has already been seen
  3406.                         bBoneSeen = FALSE;
  3407.                         for (iBoneSeen = 0; iBoneSeen < cpnBonesSeen; iBoneSeen++)
  3408.                         {
  3409.                             if (rgpnBonesSeen[iBoneSeen] == pBoneNode)
  3410.                             {
  3411.                                 bBoneSeen = TRUE;
  3412.                                 break;
  3413.                             }
  3414.                         }
  3415.                     
  3416.                         // if not seen, collect stats and add to already seen
  3417.                         if (!bBoneSeen)
  3418.                         {
  3419.                             if (cpnBonesSeen >= cpnBonesSeenMax)
  3420.                             {
  3421.                                 rgpnTemp = rgpnBonesSeen;
  3422.                                 cpnBonesSeenMax *= 2;
  3423.  
  3424.                                 rgpnBonesSeen = new INode*[cpnBonesSeenMax];
  3425.                                 if (rgpnBonesSeen == NULL)
  3426.                                 {
  3427.                                     rgpnBonesSeen = rgpnTemp;
  3428.                                     hr = E_OUTOFMEMORY;
  3429.                                     goto e_Exit;
  3430.                                 }
  3431.  
  3432.                                 memcpy(rgpnBonesSeen, rgpnTemp, cpnBonesSeen * sizeof(INode*));
  3433.                                 delete []rgpnTemp;
  3434.                             }
  3435.  
  3436.                             rgpnBonesSeen[cpnBonesSeen] = pBoneNode;
  3437.                             cpnBonesSeen += 1;
  3438.                         }
  3439.  
  3440.                         pPhyContextExport->ReleaseVertexInterface( pRigidVertexExport);
  3441.                     }
  3442.                     else
  3443.                     {
  3444.                         assert( iVertexType == RIGID_BLENDED_TYPE );
  3445.  
  3446.                         pBlendedRigidVertexExport = (IPhyBlendedRigidVertex *)pPhyContextExport->GetVertexInterface(iVertex);
  3447.                         if (pBlendedRigidVertexExport == NULL)
  3448.                         {
  3449.                             OutputDebugString("Couldn't get blended rigid vertex export interface!");
  3450.                             hr = E_FAIL;
  3451.                             goto e_Exit;
  3452.                         }
  3453.  
  3454.                         // How many nodes affect his vertex?
  3455.                         cTotalBones = pBlendedRigidVertexExport->GetNumberNodes();
  3456.                         for (iBone = 0; iBone < cTotalBones; iBone++ )
  3457.                         {
  3458.                             pBoneNode = pBlendedRigidVertexExport->GetNode(iBone);
  3459.  
  3460.                             // first see if the bone has already been seen
  3461.                             bBoneSeen = FALSE;
  3462.                             for (iBoneSeen = 0; iBoneSeen < cpnBonesSeen; iBoneSeen++)
  3463.                             {
  3464.                                 if (rgpnBonesSeen[iBoneSeen] == pBoneNode)
  3465.                                 {
  3466.                                     bBoneSeen = TRUE;
  3467.                                     break;
  3468.                                 }
  3469.                             }
  3470.                         
  3471.                             // if not seen, collect stats and add to already seen
  3472.                             if (!bBoneSeen)
  3473.                             {
  3474.                                 if (cpnBonesSeen >= cpnBonesSeenMax)
  3475.                                 {
  3476.                                     rgpnTemp = rgpnBonesSeen;
  3477.                                     cpnBonesSeenMax *= 2;
  3478.  
  3479.                                     rgpnBonesSeen = new INode*[cpnBonesSeenMax];
  3480.                                     if (rgpnBonesSeen == NULL)
  3481.                                     {
  3482.                                         rgpnBonesSeen = rgpnTemp;
  3483.                                         hr = E_OUTOFMEMORY;
  3484.                                         goto e_Exit;
  3485.                                     }
  3486.  
  3487.                                     memcpy(rgpnBonesSeen, rgpnTemp, cpnBonesSeen * sizeof(INode*));
  3488.                                     delete []rgpnTemp;
  3489.                                 }
  3490.  
  3491.                                 rgpnBonesSeen[cpnBonesSeen] = pBoneNode;
  3492.                                 cpnBonesSeen += 1;
  3493.                             }
  3494.                         }
  3495.  
  3496.                         pPhyContextExport->ReleaseVertexInterface( pBlendedRigidVertexExport);
  3497.                     }
  3498.                 }
  3499.  
  3500.                 ppc->m_cMaxWeightsPerFace = max(cpnBonesSeen, ppc->m_cMaxWeightsPerFace);
  3501.             }
  3502.         }
  3503.  
  3504. e_NoPhysique:;
  3505.     }
  3506.  
  3507.     cChildren = pNode->NumberOfChildren();
  3508.     for (iChild = 0; iChild < cChildren; iChild++)
  3509.     {
  3510.         // enumerate the child
  3511.         hr = Preprocess(ppc, pNode->GetChildNode(iChild));
  3512.         if (FAILED(hr))
  3513.             goto e_Exit;        
  3514.     }
  3515.  
  3516. e_Exit:
  3517.     if (bDeleteTriObject)
  3518.     {
  3519.         delete pTriObject;
  3520.     }
  3521.  
  3522.     delete []rgpnBonesSeen;
  3523.     return hr;
  3524. }
  3525.  
  3526.  
  3527. struct SAnimInfo
  3528. {
  3529.     DWORD dwTime;
  3530.     DWORD dwNumValues;
  3531.     float rgfValues[16];
  3532. };
  3533.  
  3534. BOOL BFloatsEqual
  3535.     (
  3536.     float f1,
  3537.     float f2
  3538.     )
  3539. {
  3540.     // first do a bitwise compare
  3541.     if ((*(DWORD*)&f1) == (*(DWORD*)&f2))
  3542.         return TRUE;
  3543.  
  3544.     // next do an epsilon compare
  3545.     float fDiff = (f1 - f2);
  3546.     return (fDiff <= 1e-6f) && (fDiff >= -1e-6f);
  3547. }
  3548.  
  3549. BOOL BEqualMatrices(float *rgfMat1, float *rgfMat2)
  3550. {
  3551.     DWORD iFloat;
  3552.  
  3553.     for (iFloat = 0; iFloat < 16; iFloat++)
  3554.     {
  3555.         if (!BFloatsEqual(rgfMat1[iFloat], rgfMat2[iFloat]))
  3556.             return FALSE;
  3557.     }
  3558.  
  3559.     return TRUE;
  3560. }
  3561.  
  3562. HRESULT GenerateAnimationSet
  3563.     (
  3564.     SSaveContext *psc
  3565.     )
  3566. {
  3567.     HRESULT hr = S_OK;
  3568.     DWORD cKeys;
  3569.     PBYTE pbData = NULL;
  3570.     PBYTE pbCur;
  3571.     DWORD cTicksPerFrame;
  3572.     DWORD iCurTime;
  3573.     DWORD iCurKey;
  3574.     Matrix3 matFirstTM;
  3575.     Matrix3 matTM;
  3576.     Matrix3 matRelativeTM;
  3577.     Matrix3 matParentTM;
  3578.     Interval interval;
  3579.     DWORD iNode;
  3580.     INode *pNode;
  3581.     DWORD cbSize;
  3582.     DWORD iKey;
  3583.     DWORD cCurrentKeys;
  3584.     SAnimInfo *rgaiAnimData = NULL;
  3585.     SAnimInfo *rgaiAnimDataCur;
  3586.     LPDIRECTXFILEDATA pAnimation = NULL;
  3587.     LPDIRECTXFILEDATA pAnimationKeys = NULL;
  3588.     char *szName;
  3589.     BOOL bAddEndKey = FALSE;
  3590.     DWORD iInterval;
  3591.     DWORD iStartTime;
  3592.     DWORD iEndTime;
  3593.     DWORD cFrameRate;
  3594.  
  3595.     // find the animation info from the interface
  3596.     interval = psc->m_pInterface->GetAnimRange();
  3597.     cTicksPerFrame = GetTicksPerFrame();
  3598.     cFrameRate = GetFrameRate();
  3599.     iStartTime = interval.Start();
  3600.     iEndTime = interval.End();
  3601.  
  3602.     iInterval = (cTicksPerFrame * cFrameRate) / psc->m_iAnimSamplingRate;
  3603.  
  3604.     cKeys = (iEndTime - iStartTime) / iInterval;
  3605.  
  3606.     // add a key for the last frame at iEndTime, or the one added if iEndTime is not directly touched
  3607.     cKeys += 1;
  3608.  
  3609.     // add a key to give the final frame a length (difference between Max and X file keyframe specification)
  3610.     cKeys += 1;
  3611.  
  3612.     // if the sampling frequency doesn't end directly on 
  3613.     //   on the end time, then add a partial end key
  3614.     if (((iEndTime - iStartTime) % iInterval) != 0)
  3615.     {
  3616.         bAddEndKey = TRUE;
  3617.     }
  3618.  
  3619.     rgaiAnimData = new SAnimInfo[psc->m_cNodes*cKeys];
  3620.     if (rgaiAnimData == NULL)
  3621.     {
  3622.         hr = E_OUTOFMEMORY;
  3623.         goto e_Exit;
  3624.     }
  3625.  
  3626.     for (iCurKey = 0, iCurTime = iStartTime; iCurTime <= iEndTime; iCurTime += iInterval, iCurKey++ )
  3627.     {
  3628.         for (iNode = 0; iNode < psc->m_cNodes; iNode++)
  3629.         {
  3630.             pNode = psc->m_rgpnNodes[iNode];
  3631.  
  3632.             //iCurTime = iFrame * (cTicksPerFrame / 2);
  3633.             matTM = pNode->GetNodeTM(iCurTime);
  3634.  
  3635.             if (pNode->GetParentNode() == NULL)
  3636.             {
  3637.                 matRelativeTM = matTM;
  3638.             }
  3639.             else
  3640.             {
  3641.                 matParentTM = pNode->GetParentNode()->GetNodeTM(iCurTime);
  3642.                 if( memcmp(&matTM,&matParentTM,12*sizeof(float)) == 0 )
  3643.                 {
  3644.                     matRelativeTM.IdentityMatrix();
  3645.                 }
  3646.                 else
  3647.                 {
  3648.                     matRelativeTM = matTM * Inverse(matParentTM);
  3649.                 }
  3650.             }
  3651.  
  3652.             // set the current pointer to the correct buffer position
  3653.             pbCur = (PBYTE)&rgaiAnimData[iNode*cKeys + iCurKey];
  3654.  
  3655.             // first write the time and dword count            
  3656.             WRITE_DWORD(pbCur, iCurTime);
  3657.             WRITE_DWORD(pbCur, 16);
  3658.  
  3659.             // next write the matrix
  3660.             WRITE_MATRIX4_FROM_MATRIX3(pbCur, matRelativeTM);
  3661.  
  3662.         }
  3663.     }
  3664.  
  3665.     // if the sampling rate doesn't evenly end on the last frame, add a partial key frame
  3666.     if (bAddEndKey)
  3667.     {
  3668.         assert(((iEndTime - iStartTime) % iInterval) != 0);
  3669.  
  3670.         // just add the end time as a key frame
  3671.         for (iNode = 0; iNode < psc->m_cNodes; iNode++)
  3672.         {
  3673.             // set the current pointer to the correct buffer position
  3674.             pbCur = (PBYTE)&rgaiAnimData[iNode*cKeys +iCurKey];
  3675.  
  3676.             matTM = pNode->GetNodeTM(iEndTime);
  3677.             if (pNode->GetParentNode() == NULL)
  3678.             {
  3679.                 matRelativeTM = matTM;
  3680.             }
  3681.             else
  3682.             {
  3683.                 matParentTM = pNode->GetParentNode()->GetNodeTM(iEndTime);
  3684.                 if( memcmp(&matTM,&matParentTM,12*sizeof(float)) == 0 )
  3685.                 {
  3686.                     matRelativeTM.IdentityMatrix();
  3687.                 }
  3688.                 else
  3689.                 {
  3690.                     matRelativeTM = matTM * Inverse(matParentTM);
  3691.                 }
  3692.             }
  3693.  
  3694.             WRITE_DWORD(pbCur, iEndTime);
  3695.             WRITE_DWORD(pbCur, 16);
  3696.  
  3697.             // next write the matrix
  3698.             WRITE_MATRIX4_FROM_MATRIX3(pbCur, matRelativeTM);
  3699.         }
  3700.  
  3701.         // update iCurKey for the looping key frames
  3702.         iCurKey += 1;
  3703.     }
  3704.  
  3705.  
  3706.     
  3707.     // add an final animation frame to allow for looping - Max assumes that the last frame lasts for an interval...  X-files do not, so specify it explicitly
  3708.     //    if looping is not desired, then this should be the final frames data instead of the first frames data
  3709.     for (iNode = 0; iNode < psc->m_cNodes; iNode++)
  3710.     {
  3711.             // set the current pointer to the correct buffer position
  3712.             pbCur = (PBYTE)&rgaiAnimData[iNode*cKeys + iCurKey];
  3713.         if (psc->m_bLoopingAnimationData)
  3714.         {
  3715.             // copy the zero'th animations data, just update the time value
  3716.             memcpy(pbCur, (PBYTE)&rgaiAnimData[iNode*cKeys + 0], sizeof(SAnimInfo));
  3717.         }
  3718.         else
  3719.         {
  3720.             // copy the previous frames data to hold for a frames worth of time
  3721.             memcpy(pbCur, (PBYTE)&rgaiAnimData[iNode*cKeys + (iCurKey-1)], sizeof(SAnimInfo));
  3722.         }
  3723.  
  3724.         WRITE_DWORD(pbCur, iEndTime + cTicksPerFrame);
  3725.     }
  3726.  
  3727.     assert(iCurKey + 1 == cKeys);    
  3728.  
  3729.     // allocate memory to send to D3DXOF lib
  3730.     cbSize = sizeof(DWORD) + sizeof(DWORD) +
  3731.             (sizeof(DWORD) //time
  3732.                 + sizeof(DWORD) //nValues
  3733.                 + sizeof(FLOAT)*16) // x, y, z
  3734.                * cKeys; // number of keys
  3735.  
  3736.     pbData = new BYTE[cbSize];
  3737.     if (pbData == NULL)
  3738.     {
  3739.         hr = E_OUTOFMEMORY;
  3740.         goto e_Exit;
  3741.     }
  3742.  
  3743.     hr = psc->m_pxofsave->CreateDataObject(TID_D3DRMAnimationSet,
  3744.                                     NULL,
  3745.                                     NULL,
  3746.                                     0,
  3747.                                     NULL,
  3748.                                     &psc->m_pAnimationSet
  3749.                                     );
  3750.     if (FAILED(hr))
  3751.         goto e_Exit;
  3752.  
  3753.     for (iNode = 0; iNode < psc->m_cNodes; iNode++)
  3754.     {
  3755.         pbCur = pbData;
  3756.  
  3757.         szName = psc->m_stStrings.CreateNiceString(psc->m_rgpnNodes[iNode]->GetName());
  3758.         if (szName == NULL)
  3759.         {
  3760.             hr = E_OUTOFMEMORY;
  3761.             goto e_Exit;
  3762.         }
  3763.  
  3764.         // write the key type
  3765.         WRITE_DWORD(pbCur, 4);
  3766.  
  3767.         // write the number of keys
  3768.         WRITE_DWORD(pbCur, cKeys);
  3769.  
  3770.         rgaiAnimDataCur = &rgaiAnimData[iNode*cKeys];
  3771.         cCurrentKeys = 0;
  3772.         for (iKey = 0; iKey < cKeys; iKey++)
  3773.         {
  3774.             memcpy(pbCur, &rgaiAnimDataCur[iKey], sizeof(SAnimInfo));
  3775.             pbCur += sizeof(SAnimInfo);
  3776.             cCurrentKeys += 1;
  3777.  
  3778.             // if not last key, then check for start of a run of identical keys
  3779.             if (iKey < (cKeys-1))
  3780.             {
  3781.                 // if equal to next, check for a run of equal matrices
  3782.                 if (BEqualMatrices(rgaiAnimDataCur[iKey].rgfValues, rgaiAnimDataCur[iKey+1].rgfValues))
  3783.                 {
  3784.                     // move to the next key, and skip all equal matrices
  3785.                     iKey += 1;
  3786.                     while ((iKey < (cKeys-1)) && BEqualMatrices(rgaiAnimDataCur[iKey].rgfValues, rgaiAnimDataCur[iKey+1].rgfValues))
  3787.                     {
  3788.                         iKey += 1;
  3789.                     }
  3790.  
  3791.                     memcpy(pbCur, &rgaiAnimDataCur[iKey], sizeof(SAnimInfo));
  3792.                     pbCur += sizeof(SAnimInfo);
  3793.                     cCurrentKeys += 1;
  3794.                 }
  3795.             }
  3796.         }
  3797.  
  3798.         // update to current count of keys
  3799.         ((DWORD*)pbData)[1] = cCurrentKeys;
  3800.  
  3801.         hr = psc->m_pxofsave->CreateDataObject(TID_D3DRMAnimation,
  3802.                                         NULL,
  3803.                                         NULL,
  3804.                                         0,
  3805.                                         NULL,
  3806.                                         &pAnimation
  3807.                                         );
  3808.  
  3809.     // add the data to the file
  3810.         hr = psc->m_pxofsave->CreateDataObject(TID_D3DRMAnimationKey,
  3811.                                         NULL,
  3812.                                         NULL,
  3813.                                         cbSize,
  3814.                                         pbData,
  3815.                                         &pAnimationKeys
  3816.                                         );
  3817.         if (FAILED(hr))
  3818.             goto e_Exit;
  3819.  
  3820.         // add to the animation set
  3821.         hr = pAnimation->AddDataObject(pAnimationKeys);
  3822.         if (FAILED(hr))
  3823.             goto e_Exit;
  3824.  
  3825.         hr = pAnimation->AddDataReference(szName, NULL);
  3826.         if (FAILED(hr))
  3827.             goto e_Exit;
  3828.  
  3829.         hr = psc->m_pAnimationSet->AddDataObject(pAnimation);
  3830.         if (FAILED(hr))
  3831.             goto e_Exit;
  3832.  
  3833.         RELEASE(pAnimation);
  3834.         RELEASE(pAnimationKeys);
  3835.     }
  3836.  
  3837. e_Exit:
  3838.     delete []rgaiAnimData;
  3839.     delete []pbData;
  3840.     RELEASE(pAnimation);
  3841.     RELEASE(pAnimationKeys);
  3842.  
  3843.     return hr;
  3844. }
  3845.  
  3846.  
  3847. // ================================================== CDataSave::CDataSave()
  3848. HRESULT ExportXFile
  3849.     (
  3850.     const TCHAR *szFilename,
  3851.     ExpInterface *pExportInterface,
  3852.     Interface *pInterface, 
  3853.     BOOL bSuppressPrompts,
  3854.     BOOL bSaveSelection,
  3855.     HWND hwndParent
  3856.     ) 
  3857. {
  3858.     LPDIRECTXFILE pxofapi = NULL;
  3859.     LPDIRECTXFILEDATA pRootData = NULL;
  3860.     LPDIRECTXFILESAVEOBJECT pxofsave = NULL; 
  3861.     HRESULT hr = S_OK;
  3862.     INode *pRootNode;
  3863.     SSaveContext sc;
  3864.     SPreprocessContext pc;
  3865.     SDialogOptions DlgOptions;
  3866.  
  3867.     assert(szFilename && pExportInterface && pInterface);
  3868.  
  3869.     // Extract scene information
  3870.     pInterface->ProgressStart(_T("Extracting skinning data"),TRUE,dummyFn,NULL);
  3871.     pInterface->ProgressUpdate(0);
  3872.     //pInterface->ProgressUpdate(100);
  3873.  
  3874.     // first find the root node
  3875.     hr = FindRootNode(pExportInterface->theScene, &pRootNode);
  3876.     if (FAILED(hr))
  3877.         goto e_Exit;
  3878.  
  3879.     // setup options for Preprocess stage
  3880.     pc.m_bSaveSelection = bSaveSelection;
  3881.  
  3882.     // figure out bone counts, etc.
  3883.     hr = Preprocess(&pc, pRootNode);
  3884.     if (FAILED(hr))
  3885.         goto e_Exit;
  3886.  
  3887.     // move the skin map data from the preprocess context to the save context
  3888.     sc.m_cpsmSkinMaps = pc.m_cpsmSkinMaps;
  3889.     sc.m_rgpsmSkinMaps = pc.m_rgpsmSkinMaps;
  3890.     pc.m_rgpsmSkinMaps = NULL;
  3891.  
  3892.     pInterface->ProgressUpdate(25);
  3893.  
  3894.     // setup dialog options
  3895.     DlgOptions.m_xFormat = DXFILEFORMAT_BINARY;
  3896.     DlgOptions.m_bSavePatchData = FALSE;
  3897.     DlgOptions.m_bSaveAnimationData = TRUE;
  3898.     DlgOptions.m_bLoopingAnimationData = TRUE;
  3899.     DlgOptions.m_iAnimSamplingRate = 30;
  3900.     DlgOptions.m_cMaxBonesPerVertex = pc.m_cMaxWeightsPerVertex;
  3901.     DlgOptions.m_cMaxBonesPerFace = pc.m_cMaxWeightsPerFace;
  3902.  
  3903.     // if prompts not suppressed, then check with the user on options
  3904.     if (!bSuppressPrompts)
  3905.     {
  3906.         DialogBoxParam(g_hInstance, 
  3907.                        MAKEINTRESOURCE(IDD_PANEL), 
  3908.                         hwndParent, 
  3909.                         XSkinExpOptionsDlgProc, 
  3910.                         (LPARAM)&DlgOptions);
  3911.  
  3912.         if (!DlgOptions.m_bProceedWithExport)
  3913.             goto e_Exit;
  3914.     }
  3915.  
  3916.     pInterface->ProgressStart(_T("Exporting data"),TRUE,dummyFn,NULL);
  3917.     pInterface->ProgressUpdate(25);
  3918.  
  3919.     // Create xofapi object.
  3920.     hr = DirectXFileCreate(&pxofapi);
  3921.     if (FAILED(hr))
  3922.         goto e_Exit;
  3923.  
  3924.     // Register templates for d3drm.
  3925.     hr = pxofapi->RegisterTemplates((LPVOID)D3DRM_XTEMPLATES,
  3926.                                     D3DRM_XTEMPLATE_BYTES);
  3927.     if (FAILED(hr))
  3928.         goto e_Exit;
  3929.  
  3930.     hr = pxofapi->RegisterTemplates((LPVOID)XSKINEXP_TEMPLATES,
  3931.                                     strlen(XSKINEXP_TEMPLATES));
  3932.     if (FAILED(hr))
  3933.         goto e_Exit;
  3934.  
  3935.     // Create save object.
  3936.     hr = pxofapi->CreateSaveObject(szFilename,    // filename
  3937.                                    DlgOptions.m_xFormat,  // binary or text
  3938.                                    &pxofsave);
  3939.     if (FAILED(hr))
  3940.         goto e_Exit;
  3941.  
  3942.     hr = pxofsave->SaveTemplates(3, aIds);
  3943.     if (FAILED(hr))
  3944.         goto e_Exit;
  3945.  
  3946.     sc.m_pxofsave = pxofsave;
  3947.     sc.m_xFormat = DlgOptions.m_xFormat;
  3948.     sc.m_bSaveAnimationData = DlgOptions.m_bSaveAnimationData;
  3949.     sc.m_bLoopingAnimationData = DlgOptions.m_bLoopingAnimationData;
  3950.     sc.m_iAnimSamplingRate = DlgOptions.m_iAnimSamplingRate;
  3951.     sc.m_bSavePatchData = DlgOptions.m_bSavePatchData;
  3952.     sc.m_iTime = pInterface->GetTime();
  3953.     sc.m_pInterface = pInterface;
  3954.     sc.m_bSaveSelection = bSaveSelection;
  3955.     sc.m_cMaxWeightsPerVertex = pc.m_cMaxWeightsPerVertex;
  3956.     sc.m_cMaxWeightsPerFace = pc.m_cMaxWeightsPerFace;
  3957.  
  3958.     sc.m_cNodes = pc.m_cNodes;
  3959.     sc.m_cNodesCur = 0;
  3960.     sc.m_rgpnNodes = new INode*[sc.m_cNodes];
  3961.     if (sc.m_rgpnNodes == NULL)
  3962.     {
  3963.         hr = E_OUTOFMEMORY;
  3964.         goto e_Exit;
  3965.     }
  3966.  
  3967.     // then write the whole tree out into file data's
  3968.     hr = EnumTree(&sc, pRootNode, &pRootData);
  3969.     if (FAILED(hr))
  3970.         goto e_Exit;
  3971.  
  3972.     pInterface->ProgressStart(_T("Saving data to file"),TRUE,dummyFn,NULL);
  3973.     pInterface->ProgressUpdate(50);
  3974.  
  3975.     // now save that file data to the file
  3976.     hr = pxofsave->SaveData(pRootData);
  3977.     if (FAILED(hr))
  3978.         goto e_Exit;
  3979.  
  3980.     pInterface->ProgressUpdate(75);
  3981.  
  3982.     if (DlgOptions.m_bSaveAnimationData)
  3983.     {
  3984.         pInterface->ProgressStart(_T("Saving animation data to file"),TRUE,dummyFn,NULL);
  3985.         pInterface->ProgressUpdate(75);
  3986.  
  3987.         hr = GenerateAnimationSet(&sc);
  3988.         if (FAILED(hr))
  3989.             goto e_Exit;
  3990.  
  3991.         hr = pxofsave->SaveData(sc.m_pAnimationSet);
  3992.         if (FAILED(hr))
  3993.         {
  3994.             OutputDebugString("Failed to add animation set to X File\n");
  3995.             goto e_Exit;
  3996.         }            
  3997.     }
  3998.  
  3999. e_Exit:
  4000.     if (FAILED(hr))
  4001.     {
  4002.         OutputDebugString("File was not successfully exported.");
  4003.         {
  4004.             TCHAR errstr[500 + _MAX_PATH];
  4005.             _stprintf(errstr,"Could not write to file: %s.\n"\
  4006.                 "Try checking the file's permissions, or if it is currently open.",szFilename);
  4007.             MessageBox(hwndParent,errstr,_T("Error!"),MB_OK);
  4008.         }
  4009.     }
  4010.  
  4011.     // falling through
  4012.     // Free up outstanding interfaces
  4013.     RELEASE(pxofsave);
  4014.     RELEASE(pRootData);
  4015.     RELEASE(pxofapi);
  4016.  
  4017.     pInterface->ProgressUpdate(100);
  4018.     pInterface->ProgressEnd();
  4019.  
  4020.     return hr;
  4021. }
  4022.  
  4023.